From 484a6c7b88e6578dbe5dbdd74b6cc1753900a985 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:17:22 +0300 Subject: [PATCH 01/42] refactor(shared): extract task contracts and model policy Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../agent/src/adapters/reasoning-effort.ts | 45 +- packages/agent/src/gateway-models.test.ts | 252 +----------- packages/agent/src/gateway-models.ts | 274 ++----------- packages/agent/src/utils/gateway.ts | 23 +- packages/api-client/src/posthog-client.ts | 2 +- packages/shared/src/cloud-task-models.test.ts | 200 +++++++++ packages/shared/src/cloud-task-models.ts | 385 ++++++++++++++++++ packages/shared/src/domain-types.test.ts | 19 +- packages/shared/src/domain-types.ts | 34 +- packages/shared/src/index.ts | 74 +++- packages/shared/src/reasoning-effort.test.ts | 20 + packages/shared/src/reasoning-effort.ts | 77 ++++ packages/shared/src/sessions.ts | 2 +- packages/shared/src/task-automation.test.ts | 66 +++ packages/shared/src/task-automation.ts | 58 +++ packages/shared/src/task.test.ts | 53 +++ packages/shared/src/task.ts | 103 +---- .../src/services/agent/agent.ts | 117 +----- 18 files changed, 1035 insertions(+), 769 deletions(-) create mode 100644 packages/shared/src/cloud-task-models.test.ts create mode 100644 packages/shared/src/cloud-task-models.ts create mode 100644 packages/shared/src/reasoning-effort.test.ts create mode 100644 packages/shared/src/reasoning-effort.ts create mode 100644 packages/shared/src/task-automation.test.ts create mode 100644 packages/shared/src/task-automation.ts create mode 100644 packages/shared/src/task.test.ts diff --git a/packages/agent/src/adapters/reasoning-effort.ts b/packages/agent/src/adapters/reasoning-effort.ts index 590bb3be86..2cc1da50f8 100644 --- a/packages/agent/src/adapters/reasoning-effort.ts +++ b/packages/agent/src/adapters/reasoning-effort.ts @@ -1,39 +1,6 @@ -import type { Adapter } from "@posthog/shared"; -import { getEffortOptions as getClaudeEffortOptions } from "./claude/session/models"; -import { getReasoningEffortOptions as getCodexReasoningEffortOptions } from "./codex-app-server/models"; - -export type SupportedReasoningEffort = - | "low" - | "medium" - | "high" - | "xhigh" - | "max"; - -export interface ReasoningEffortOption { - value: SupportedReasoningEffort; - name: string; -} - -export function getReasoningEffortOptions( - adapter: Adapter, - modelId: string, -): ReasoningEffortOption[] | null { - const options = - adapter === "codex" - ? getCodexReasoningEffortOptions(modelId) - : getClaudeEffortOptions(modelId); - - return options as ReasoningEffortOption[] | null; -} - -export function isSupportedReasoningEffort( - adapter: Adapter, - modelId: string, - value: string, -): value is SupportedReasoningEffort { - return ( - getReasoningEffortOptions(adapter, modelId)?.some( - (option) => option.value === value, - ) ?? false - ); -} +export { + getReasoningEffortOptions, + isSupportedReasoningEffort, + type ReasoningEffortOption, + type SupportedReasoningEffort, +} from "@posthog/shared"; diff --git a/packages/agent/src/gateway-models.test.ts b/packages/agent/src/gateway-models.test.ts index 5aa7438a8d..8c988ec1e9 100644 --- a/packages/agent/src/gateway-models.test.ts +++ b/packages/agent/src/gateway-models.test.ts @@ -1,163 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { - compareModelsForPicker, - fetchGatewayModels, - fetchModelsList, - formatGatewayModelName, - type GatewayModel, - getClaudeModelRecency, - isAnthropicModel, - isBlockedModelId, - isCloudflareModel, - pickAllowedModel, -} from "./gateway-models"; - -const model = (id: string, owned_by = ""): GatewayModel => ({ - id, - owned_by, - context_window: 128000, - supports_streaming: true, - supports_vision: false, - allowed: true, -}); - -describe("formatGatewayModelName", () => { - it("keeps Claude models in friendly title case", () => { - expect( - formatGatewayModelName({ - id: "claude-opus-4-8", - owned_by: "anthropic", - context_window: 200000, - supports_streaming: true, - supports_vision: true, - allowed: true, - }), - ).toBe("Claude Opus 4.8"); - }); - - it("uppercases the GPT acronym in OpenAI model ids", () => { - expect( - formatGatewayModelName({ - id: "GPT-5.5", - owned_by: "openai", - context_window: 200000, - supports_streaming: true, - supports_vision: true, - allowed: true, - }), - ).toBe("GPT-5.5"); - }); - - it("strips the openai/ prefix, uppercases GPT, and title-cases the suffix", () => { - expect( - formatGatewayModelName({ - id: "openai/gpt-5.6-sol", - owned_by: "openai", - context_window: 200000, - supports_streaming: true, - supports_vision: true, - allowed: true, - }), - ).toBe("GPT-5.6 Sol"); - }); - - it("formats Cloudflare models as the final path segment with GLM uppercased", () => { - expect( - formatGatewayModelName({ - id: "@cf/zai-org/glm-5.2", - owned_by: "cloudflare", - context_window: 128000, - supports_streaming: true, - supports_vision: false, - allowed: true, - }), - ).toBe("GLM-5.2"); - }); - - it("leaves non-acronym Cloudflare models lowercase", () => { - expect( - formatGatewayModelName({ - id: "@cf/meta/llama-3.1-8b-instruct", - owned_by: "cloudflare", - context_window: 128000, - supports_streaming: true, - supports_vision: false, - allowed: true, - }), - ).toBe("llama-3.1-8b-instruct"); - }); - - it("blocks deprecated Claude gateway models", () => { - expect(isBlockedModelId("claude-opus-4-5")).toBe(true); - expect(isBlockedModelId("claude-opus-4-6")).toBe(true); - expect(isBlockedModelId("claude-sonnet-4-5")).toBe(true); - expect(isBlockedModelId("claude-haiku-4-5")).toBe(true); - expect(isBlockedModelId("ANTHROPIC/CLAUDE-HAIKU-4-5")).toBe(true); - }); - - it("blocks deprecated Codex gateway models", () => { - expect(isBlockedModelId("gpt-5.2")).toBe(true); - expect(isBlockedModelId("gpt-5.3")).toBe(true); - expect(isBlockedModelId("gpt-5.3-codex")).toBe(true); - expect(isBlockedModelId("openai/gpt-5.2")).toBe(true); - expect(isBlockedModelId("OPENAI/GPT-5.3")).toBe(true); - expect(isBlockedModelId("OPENAI/GPT-5.3-CODEX")).toBe(true); - }); -}); - -describe("getClaudeModelRecency", () => { - it.each([ - ["claude-haiku-4-5", 4005], - ["claude-sonnet-4-6", 4006], - ["claude-opus-4-7", 4007], - ["claude-opus-4-8", 4008], - ["claude-opus-5", 5000], - ["claude-sonnet-5", 5000], - ["claude-fable-5", 5000], - ])("ranks %s by its embedded version (%i)", (modelId, rank) => { - expect(getClaudeModelRecency(modelId)).toBe(rank); - }); - - it("ignores a trailing date suffix when reading the version", () => { - expect(getClaudeModelRecency("claude-haiku-4-5-20251001")).toBe(4005); - }); - - it("ranks a model with no recognisable version as newest", () => { - expect(getClaudeModelRecency("claude-mystery")).toBe( - Number.MAX_SAFE_INTEGER, - ); - expect(getClaudeModelRecency("claude-mystery")).toBeGreaterThan( - getClaudeModelRecency("claude-fable-5"), - ); - }); -}); - -describe("compareModelsForPicker", () => { - it("groups models by family least capable first, newest version first", () => { - // The picker opens upward, so least-capable-first DOM order puts the most - // capable family (Fable) nearest the trigger — the visual top of the menu. - // Models as the gateway might return them — arbitrary order. - const gatewayOrder = [ - "claude-fable-5", - "claude-opus-4-7", - "claude-mystery", - "claude-sonnet-5", - "claude-haiku-4-5", - "claude-sonnet-4-6", - "claude-opus-4-8", - ]; - const displayed = [...gatewayOrder].sort(compareModelsForPicker); - expect(displayed).toEqual([ - "claude-haiku-4-5", - "claude-sonnet-5", - "claude-sonnet-4-6", - "claude-opus-4-8", - "claude-opus-4-7", - "claude-fable-5", - "claude-mystery", - ]); - }); -}); +import { fetchGatewayModels, fetchModelsList } from "./gateway-models"; describe("gateway model fetch timeout", () => { afterEach(() => { @@ -242,96 +84,4 @@ describe("gateway models cache", () => { expect(fetchMock).toHaveBeenCalledTimes(1); expect(cached[0]?.allowed).toBe(false); }); - - it("corrects stale GLM 5.2 context-window metadata", async () => { - vi.spyOn(globalThis, "fetch").mockResolvedValue( - new Response( - JSON.stringify({ - object: "list", - data: [ - { - id: "@cf/zai-org/glm-5.2", - owned_by: "cloudflare", - context_window: 128_000, - supports_streaming: true, - supports_vision: false, - }, - ], - }), - { status: 200, headers: { "Content-Type": "application/json" } }, - ), - ); - - const models = await fetchGatewayModels({ - gatewayUrl: "https://gateway.glm-context-test", - }); - - expect(models[0]?.context_window).toBe(1_000_000); - }); -}); - -describe("isCloudflareModel", () => { - it.each([ - { id: "@cf/zai-org/glm-5.2", owned_by: "cloudflare", expected: true }, - { id: "claude-opus-4-8", owned_by: "anthropic", expected: false }, - { id: "@cf/zai-org/glm-5.2", owned_by: "", expected: true }, - { id: "gpt-5.5", owned_by: "", expected: false }, - // A Cloudflare-served model can report an upstream owner; the `@cf/` prefix still wins. - { id: "@cf/openai/gpt-oss", owned_by: "openai", expected: true }, - ])( - "isCloudflareModel($id, owned_by=$owned_by) → $expected", - ({ id, owned_by, expected }) => { - expect(isCloudflareModel(model(id, owned_by))).toBe(expected); - }, - ); - - it("does not classify Cloudflare models as Anthropic", () => { - // The Claude adapter accepts both, but they must stay distinguishable. - const glm = model("@cf/zai-org/glm-5.2", "cloudflare"); - expect(isCloudflareModel(glm)).toBe(true); - expect(isAnthropicModel(glm)).toBe(false); - }); -}); - -describe("pickAllowedModel", () => { - const entry = (id: string, allowed: boolean) => ({ id, allowed }); - - it.each([ - [ - "keeps an allowed preferred model", - [entry("claude-opus-4-8", true)], - "claude-opus-4-8", - "claude-opus-4-8", - ], - [ - "keeps a preferred model absent from the list", - [entry("claude-opus-4-8", true)], - "claude-sonnet-5", - "claude-sonnet-5", - ], - [ - "moves a restricted preferred model to the newest allowed one", - [ - entry("claude-opus-4-8", false), - entry("claude-sonnet-4-6", true), - entry("@cf/zai-org/glm-5.2", true), - ], - "claude-opus-4-8", - "@cf/zai-org/glm-5.2", - ], - [ - "keeps the preferred model when everything is restricted", - [entry("claude-opus-4-8", false)], - "claude-opus-4-8", - "claude-opus-4-8", - ], - [ - "keeps the preferred model when the list is empty", - [], - "claude-opus-4-8", - "claude-opus-4-8", - ], - ] as const)("%s", (_name, models, preferred, expected) => { - expect(pickAllowedModel(models, preferred)).toBe(expected); - }); }); diff --git a/packages/agent/src/gateway-models.ts b/packages/agent/src/gateway-models.ts index 116d9bd7d1..35ae10df5a 100644 --- a/packages/agent/src/gateway-models.ts +++ b/packages/agent/src/gateway-models.ts @@ -1,20 +1,28 @@ -export interface GatewayModel { - id: string; - owned_by: string; - context_window: number; - supports_streaming: boolean; - supports_vision: boolean; - // Free-tier model gate: authenticated fetches mark models outside the - // caller's plan `allowed: false`. Anonymous fetches and older gateways - // don't mark, so absence means allowed. - allowed: boolean; - restriction_reason?: string | null; -} - -interface GatewayModelsResponse { - object: "list"; - data: Array & { allowed?: boolean }>; -} +import { + type GatewayModel, + normalizeGatewayModelsResponse, +} from "@posthog/shared"; + +export { + BLOCKED_GATEWAY_MODEL_IDS, + buildCloudTaskConfigOptions, + type CloudTaskConfigOption, + type CloudTaskConfigSelectOption, + compareModelsForPicker, + DEFAULT_CODEX_MODEL, + DEFAULT_GATEWAY_MODEL, + formatGatewayModelName, + formatModelId, + type GatewayModel, + getClaudeModelRecency, + getProviderName, + isAnthropicModel, + isBlockedModelId, + isCloudflareModel, + isCloudflareModelId, + isOpenAIModel, + pickAllowedModel, +} from "@posthog/shared"; export interface FetchGatewayModelsOptions { gatewayUrl: string; @@ -22,47 +30,6 @@ export interface FetchGatewayModelsOptions { authToken?: string; } -export const DEFAULT_GATEWAY_MODEL = "claude-opus-4-8"; - -export const DEFAULT_CODEX_MODEL = "gpt-5.5"; - -const BLOCKED_MODELS = new Set([ - "gpt-5-mini", - "openai/gpt-5-mini", - "gpt-5.2", - "openai/gpt-5.2", - "gpt-5.3", - "openai/gpt-5.3", - "gpt-5.3-codex", - "openai/gpt-5.3-codex", - "claude-opus-4-5", - "anthropic/claude-opus-4-5", - "claude-opus-4-6", - "anthropic/claude-opus-4-6", - "claude-sonnet-4-5", - "anthropic/claude-sonnet-4-5", - "claude-haiku-4-5", - "anthropic/claude-haiku-4-5", -]); - -export function isBlockedModelId(modelId: string): boolean { - return BLOCKED_MODELS.has(modelId.toLowerCase()); -} - -interface ModelsListEntry { - id?: string; - owned_by?: string; - allowed?: boolean; - restriction_reason?: string | null; -} - -type ModelsListResponse = - | { - data?: ModelsListEntry[]; - models?: ModelsListEntry[]; - } - | ModelsListEntry[]; - const CACHE_TTL = 10 * 60 * 1000; // 10 minutes // Bound the gateway /v1/models request so a stalled connection cannot hold up @@ -71,10 +38,6 @@ const CACHE_TTL = 10 * 60 * 1000; // 10 minutes // the callers fall through to `return []`. const GATEWAY_FETCH_TIMEOUT_MS = 10_000; -const MODEL_CONTEXT_WINDOW_OVERRIDES: Readonly> = { - "@cf/zai-org/glm-5.2": 1_000_000, -}; - // Restriction marks are identity-scoped (free-tier marks are authed-only and // differ per org), so cache entries are keyed on the exact token — an org // switch in the same process must never be served the old org's marks. A @@ -125,17 +88,7 @@ export async function fetchGatewayModels( return []; } - const data = (await response.json()) as GatewayModelsResponse; - const models = (data.data ?? []) - .filter((m) => !isBlockedModelId(m.id)) - .map((m) => ({ - ...m, - context_window: Math.max( - m.context_window, - MODEL_CONTEXT_WINDOW_OVERRIDES[m.id] ?? 0, - ), - allowed: m.allowed !== false, - })); + const models = normalizeGatewayModelsResponse(await response.json()); gatewayModelsCache = { models, expiry: Date.now() + CACHE_TTL, @@ -148,36 +101,6 @@ export async function fetchGatewayModels( } } -export function isAnthropicModel(model: GatewayModel): boolean { - if (model.owned_by) { - return model.owned_by === "anthropic"; - } - return model.id.startsWith("claude-") || model.id.startsWith("anthropic/"); -} - -export function isOpenAIModel(model: GatewayModel): boolean { - if (model.owned_by) { - return model.owned_by === "openai"; - } - return model.id.startsWith("gpt-") || model.id.startsWith("openai/"); -} - -// Cloudflare Workers AI model ids carry the `@cf/` path prefix (e.g. `@cf/zai-org/glm-5.2`). Kept as -// a standalone id-only check so callers that only have a model id (not a full GatewayModel) — like the -// Claude adapter's desync guard — share one source of truth with `isCloudflareModel`. -export function isCloudflareModelId(modelId: string): boolean { - return modelId.startsWith("@cf/"); -} - -// Cloudflare Workers AI models (e.g. `@cf/zai-org/glm-5.2`). The gateway serves these over both its -// OpenAI and Anthropic-Messages surfaces (it translates the `@cf/` path), so the Claude adapter can -// drive them just like an Anthropic model. The `@cf/` path prefix is the structural, always-present -// signal, so honour it regardless of `owned_by` — a Cloudflare-served model can report an upstream -// owner (e.g. `@cf/openai/...` with `owned_by: "openai"`) and must still classify as Cloudflare. -export function isCloudflareModel(model: GatewayModel): boolean { - return isCloudflareModelId(model.id) || model.owned_by === "cloudflare"; -} - export interface ModelInfo { id: string; owned_by?: string; @@ -208,22 +131,14 @@ export async function fetchModelsList( if (!response.ok) { return []; } - const data = (await response.json()) as ModelsListResponse; - const models = Array.isArray(data) - ? data - : (data.data ?? data.models ?? []); - const results: ModelInfo[] = []; - for (const model of models) { - const id = model?.id ? String(model.id) : ""; - if (!id) continue; - if (isBlockedModelId(id)) continue; - results.push({ - id, - owned_by: model?.owned_by, - allowed: model?.allowed !== false, - restriction_reason: model?.restriction_reason ?? null, - }); - } + const results = normalizeGatewayModelsResponse(await response.json()).map( + (model) => ({ + id: model.id, + owned_by: model.owned_by || undefined, + allowed: model.allowed, + restriction_reason: model.restriction_reason, + }), + ); modelsListCache = { models: results, expiry: Date.now() + CACHE_TTL, @@ -235,124 +150,3 @@ export async function fetchModelsList( return []; } } - -/** - * The model a session should start on: the preferred id when present and - * allowed, else the newest allowed model — a free-tier org must not default - * onto a model that 403s its first message. Falls back to the preferred id - * when the list is empty (fetch failed) or nothing is allowed (all locked — - * the picker gate communicates that state better than a silent swap). - */ -export function pickAllowedModel( - models: ReadonlyArray>, - preferred: string, -): string { - if (models.length === 0) return preferred; - const preferredEntry = models.find((m) => m.id === preferred); - if (!preferredEntry || preferredEntry.allowed) return preferred; - const allowed = models.filter((m) => m.allowed); - if (allowed.length === 0) return preferred; - return allowed.reduce((best, candidate) => - getClaudeModelRecency(candidate.id) >= getClaudeModelRecency(best.id) - ? candidate - : best, - ).id; -} - -const PROVIDER_NAMES: Record = { - anthropic: "Anthropic", - openai: "OpenAI", - "google-vertex": "Gemini", -}; - -export function getProviderName(ownedBy: string): string { - return PROVIDER_NAMES[ownedBy] ?? ownedBy; -} - -// Version embedded in the model id, e.g. "claude-opus-4-8" -> 4008. Ids with no -// recognisable version rank newest. A trailing date suffix is ignored. -export function getClaudeModelRecency(modelId: string): number { - const match = modelId.toLowerCase().match(/-(\d+)(?:[-.](\d+))?/); - if (!match) return Number.MAX_SAFE_INTEGER; - const major = Number(match[1]); - const minor = match[2] ? Number(match[2]) : 0; - return major * 1000 + minor; -} - -// Families ordered least-capable first. The picker opens upward (side="top") -// from the composer, so items later in this list render nearer the trigger and -// read as the top of the menu — this puts the most-capable family (Fable) on -// top. Unknown families sort after all known ones. -const MODEL_FAMILY_ORDER = ["haiku", "sonnet", "opus", "fable"]; - -function getModelFamilyRank(modelId: string): number { - const id = modelId.toLowerCase(); - const index = MODEL_FAMILY_ORDER.findIndex((family) => id.includes(family)); - return index === -1 ? MODEL_FAMILY_ORDER.length : index; -} - -// Group by family, then newest version first within each family. -export function compareModelsForPicker(a: string, b: string): number { - const familyDiff = getModelFamilyRank(a) - getModelFamilyRank(b); - if (familyDiff !== 0) return familyDiff; - return getClaudeModelRecency(b) - getClaudeModelRecency(a); -} - -const PROVIDER_PREFIXES = ["anthropic/", "openai/", "google-vertex/"]; - -const KNOWN_ACRONYMS = new Set(["gpt", "glm"]); - -// For a known acronym, uppercase it, keep the version attached, and title-case -// any suffix: "gpt-5.6-sol" -> "GPT-5.6 Sol", "glm-5.2" -> "GLM-5.2". Other ids -// stay lowercase to avoid mangling ordinary names (e.g. "llama-3.1-8b"). -function formatProviderModelName(modelId: string): string { - const [acronym, version, ...suffix] = modelId.split("-"); - if (!KNOWN_ACRONYMS.has(acronym.toLowerCase())) return modelId.toLowerCase(); - const head = version - ? `${acronym.toUpperCase()}-${version}` - : acronym.toUpperCase(); - const tail = suffix.map( - (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), - ); - return [head, ...tail].join(" "); -} - -export function formatGatewayModelName(model: GatewayModel): string { - if (isCloudflareModel(model)) { - return formatProviderModelName(model.id.split("/").pop() ?? model.id); - } - - if (isOpenAIModel(model)) { - return formatProviderModelName(stripProviderPrefix(model.id)); - } - - return formatModelId(model.id); -} - -function stripProviderPrefix(modelId: string): string { - for (const prefix of PROVIDER_PREFIXES) { - if (modelId.startsWith(prefix)) { - return modelId.slice(prefix.length); - } - } - return modelId; -} - -export function formatModelId(modelId: string): string { - let cleanId = modelId; - for (const prefix of PROVIDER_PREFIXES) { - if (cleanId.startsWith(prefix)) { - cleanId = cleanId.slice(prefix.length); - break; - } - } - - cleanId = cleanId.replace(/(\d)-(\d)/g, "$1.$2"); - - const words = cleanId.split(/[-_]/).map((word) => { - if (word.match(/^[0-9.]+$/)) return word; - return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); - }); - - return words.join(" "); -} diff --git a/packages/agent/src/utils/gateway.ts b/packages/agent/src/utils/gateway.ts index b258086070..5e97841742 100644 --- a/packages/agent/src/utils/gateway.ts +++ b/packages/agent/src/utils/gateway.ts @@ -1,3 +1,5 @@ +import { getCloudTaskGatewayUrl } from "@posthog/shared"; + export type GatewayProduct = | "posthog_code" | "background_agents" @@ -37,26 +39,7 @@ export { } from "@posthog/shared/posthog-property-headers"; function getGatewayBaseUrl(posthogHost: string): string { - const url = new URL(posthogHost); - const hostname = url.hostname; - - if (hostname === "localhost" || hostname === "127.0.0.1") { - return `${url.protocol}//localhost:3308`; - } - - if (hostname === "host.docker.internal") { - return `${url.protocol}//host.docker.internal:3308`; - } - - // The hosted dev environment runs its own LLM gateway with its own auth DB, - // so a dev-minted `pha_` token can't be routed to the US gateway — that's - // a different DB and returns 401 Authentication required. - if (hostname === "app.dev.posthog.dev") { - return "https://gateway.dev.posthog.dev"; - } - - const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us"; - return `https://gateway.${region}.posthog.com`; + return getCloudTaskGatewayUrl(posthogHost).replace(/\/posthog_code$/, ""); } export function getLlmGatewayUrl( diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index 00bbbc7700..906e77f290 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -1,5 +1,4 @@ import "./generated.augment"; -import { isSupportedReasoningEffort } from "@posthog/agent/adapters/reasoning-effort"; import type { Adapter, CloudMcpServerImport, @@ -15,6 +14,7 @@ import type { import { DISMISSAL_REASON_OPTIONS, type DismissalReasonOptionValue, + isSupportedReasoningEffort, resolveCloudInitialPermissionMode, } from "@posthog/shared"; import type { diff --git a/packages/shared/src/cloud-task-models.test.ts b/packages/shared/src/cloud-task-models.test.ts new file mode 100644 index 0000000000..31418a7775 --- /dev/null +++ b/packages/shared/src/cloud-task-models.test.ts @@ -0,0 +1,200 @@ +import { describe, expect, it } from "vitest"; +import { + buildCloudTaskConfigOptions, + compareModelsForPicker, + formatGatewayModelName, + type GatewayModel, + getClaudeModelRecency, + isAnthropicModel, + isBlockedModelId, + isCloudflareModel, + normalizeGatewayModelsResponse, + pickAllowedModel, +} from "./cloud-task-models"; + +const model = ( + id: string, + owned_by = "anthropic", + allowed = true, +): GatewayModel => ({ + id, + owned_by, + context_window: 128000, + supports_streaming: true, + supports_vision: false, + allowed, +}); + +describe("formatGatewayModelName", () => { + it.each([ + [model("claude-opus-4-8"), "Claude Opus 4.8"], + [model("GPT-5.5", "openai"), "GPT-5.5"], + [model("openai/gpt-5.6-sol", "openai"), "GPT-5.6 Sol"], + [model("@cf/zai-org/glm-5.2", "cloudflare"), "GLM-5.2"], + [ + model("@cf/meta/llama-3.1-8b-instruct", "cloudflare"), + "llama-3.1-8b-instruct", + ], + ])("formats $id", (gatewayModel, expected) => { + expect(formatGatewayModelName(gatewayModel)).toBe(expected); + }); +}); + +describe("normalizeGatewayModelsResponse", () => { + it("corrects stale GLM 5.2 context-window metadata", () => { + const models = normalizeGatewayModelsResponse([ + model("@cf/zai-org/glm-5.2", "cloudflare"), + ]); + + expect(models[0]?.context_window).toBe(1_000_000); + }); +}); + +describe("isBlockedModelId", () => { + it.each([ + "claude-opus-4-5", + "claude-opus-4-6", + "claude-sonnet-4-5", + "ANTHROPIC/CLAUDE-HAIKU-4-5", + "gpt-5.2", + "gpt-5.3", + "gpt-5.3-codex", + "OPENAI/GPT-5.3-CODEX", + ])("blocks %s", (modelId) => { + expect(isBlockedModelId(modelId)).toBe(true); + }); +}); + +describe("getClaudeModelRecency", () => { + it.each([ + ["claude-haiku-4-5", 4005], + ["claude-sonnet-4-6", 4006], + ["claude-opus-4-7", 4007], + ["claude-opus-4-8", 4008], + ["claude-sonnet-5", 5000], + ])("ranks %s", (modelId, expected) => { + expect(getClaudeModelRecency(modelId)).toBe(expected); + }); + + it("ignores trailing dates and ranks unknown versions newest", () => { + expect(getClaudeModelRecency("claude-haiku-4-5-20251001")).toBe(4005); + expect(getClaudeModelRecency("claude-mystery")).toBe( + Number.MAX_SAFE_INTEGER, + ); + }); +}); + +describe("compareModelsForPicker", () => { + it("groups by capability and sorts newest first", () => { + const displayed = [ + "claude-fable-5", + "claude-opus-4-7", + "claude-mystery", + "claude-sonnet-5", + "claude-haiku-4-5", + "claude-sonnet-4-6", + "claude-opus-4-8", + ].sort(compareModelsForPicker); + + expect(displayed).toEqual([ + "claude-fable-5", + "claude-opus-4-8", + "claude-opus-4-7", + "claude-sonnet-5", + "claude-sonnet-4-6", + "claude-haiku-4-5", + "claude-mystery", + ]); + }); +}); + +describe("model classification", () => { + it("keeps Cloudflare models distinct from Anthropic", () => { + const gatewayModel = model("@cf/openai/gpt-oss", "openai"); + expect(isCloudflareModel(gatewayModel)).toBe(true); + expect(isAnthropicModel(gatewayModel)).toBe(false); + }); +}); + +describe("pickAllowedModel", () => { + const entry = (id: string, allowed: boolean) => ({ id, allowed }); + + it.each([ + [[entry("claude-opus-4-8", true)], "claude-opus-4-8", "claude-opus-4-8"], + [[entry("claude-opus-4-8", true)], "claude-sonnet-5", "claude-sonnet-5"], + [ + [ + entry("claude-opus-4-8", false), + entry("claude-sonnet-4-6", true), + entry("@cf/zai-org/glm-5.2", true), + ], + "claude-opus-4-8", + "@cf/zai-org/glm-5.2", + ], + [[entry("claude-opus-4-8", false)], "claude-opus-4-8", "claude-opus-4-8"], + [[], "claude-opus-4-8", "claude-opus-4-8"], + ] as const)("selects an allowed default", (models, preferred, expected) => { + expect(pickAllowedModel(models, preferred)).toBe(expected); + }); +}); + +describe("buildCloudTaskConfigOptions", () => { + it("builds Claude options with restrictions and reasoning policy", () => { + const options = buildCloudTaskConfigOptions( + [ + model("gpt-5.5", "openai"), + model("claude-opus-4-7", "anthropic"), + model("claude-opus-4-8", "anthropic", false), + model("@cf/zai-org/glm-5.2", "cloudflare"), + ], + "claude", + ); + + expect(options).toMatchObject([ + { id: "mode", currentValue: "plan" }, + { + id: "model", + currentValue: "@cf/zai-org/glm-5.2", + options: [ + { value: "claude-opus-4-7" }, + { + value: "claude-opus-4-8", + _meta: { "posthog.code/restrictedModel": true }, + }, + { value: "@cf/zai-org/glm-5.2" }, + ], + }, + ]); + expect(options.map((option) => option.id)).toEqual(["mode", "model"]); + }); + + it("builds Codex options with the shared default and reasoning levels", () => { + const options = buildCloudTaskConfigOptions( + [ + model("claude-opus-4-8"), + model("gpt-5.6", "openai"), + model("gpt-5.5", "openai"), + ], + "codex", + ); + + expect(options).toMatchObject([ + { id: "mode", currentValue: "auto" }, + { + id: "model", + currentValue: "gpt-5.5", + options: [{ value: "gpt-5.6" }, { value: "gpt-5.5" }], + }, + { + id: "reasoning_effort", + currentValue: "high", + options: [ + { value: "low" }, + { value: "medium" }, + { value: "high" }, + { value: "xhigh" }, + ], + }, + ]); + }); +}); diff --git a/packages/shared/src/cloud-task-models.ts b/packages/shared/src/cloud-task-models.ts new file mode 100644 index 0000000000..a0fc5f4477 --- /dev/null +++ b/packages/shared/src/cloud-task-models.ts @@ -0,0 +1,385 @@ +import type { Adapter } from "./adapter"; +import { CODEX_MODE_PRESETS } from "./execution-modes"; +import { restrictedModelMeta } from "./models"; +import { getReasoningEffortOptions } from "./reasoning-effort"; + +export interface GatewayModel { + id: string; + owned_by: string; + context_window: number; + supports_streaming: boolean; + supports_vision: boolean; + allowed: boolean; + restriction_reason?: string | null; +} + +interface GatewayModelsResponse { + data?: unknown[]; + models?: unknown[]; +} + +export interface CloudTaskConfigSelectOption { + value: string; + name: string; + description?: string; + _meta?: Record; +} + +export interface CloudTaskConfigOption { + id: string; + name: string; + type: "select"; + currentValue: string; + options: CloudTaskConfigSelectOption[]; + category: "mode" | "model" | "thought_level"; + description: string; +} + +export interface CloudTaskModePreset { + id: string; + name: string; + description: string; +} + +export const DEFAULT_GATEWAY_MODEL = "claude-opus-4-8"; + +export const DEFAULT_CODEX_MODEL = "gpt-5.5"; + +export const BLOCKED_GATEWAY_MODEL_IDS = [ + "gpt-5-mini", + "openai/gpt-5-mini", + "gpt-5.2", + "openai/gpt-5.2", + "gpt-5.3", + "openai/gpt-5.3", + "gpt-5.3-codex", + "openai/gpt-5.3-codex", + "claude-opus-4-5", + "anthropic/claude-opus-4-5", + "claude-opus-4-6", + "anthropic/claude-opus-4-6", + "claude-sonnet-4-5", + "anthropic/claude-sonnet-4-5", + "claude-haiku-4-5", + "anthropic/claude-haiku-4-5", +] as const; + +const BLOCKED_GATEWAY_MODELS = new Set(BLOCKED_GATEWAY_MODEL_IDS); + +const CLAUDE_MODE_PRESETS: readonly CloudTaskModePreset[] = [ + { + id: "default", + name: "Default", + description: "Standard behavior, prompts for dangerous operations", + }, + { + id: "acceptEdits", + name: "Accept Edits", + description: "Auto-accept file edit operations", + }, + { + id: "plan", + name: "Plan Mode", + description: "Planning mode, no actual tool execution", + }, + { + id: "bypassPermissions", + name: "Bypass Permissions", + description: "Auto-accept all permission requests", + }, + { + id: "auto", + name: "Auto Mode", + description: "Auto-approve file edits and shell commands", + }, +]; + +const PROVIDER_NAMES: Record = { + anthropic: "Anthropic", + openai: "OpenAI", + "google-vertex": "Gemini", +}; + +const MODEL_FAMILY_ORDER = ["fable", "opus", "sonnet", "haiku"]; +const PROVIDER_PREFIXES = ["anthropic/", "openai/", "google-vertex/"]; +const KNOWN_ACRONYMS = new Set(["gpt", "glm"]); +const MODEL_CONTEXT_WINDOW_OVERRIDES: Readonly> = { + "@cf/zai-org/glm-5.2": 1_000_000, +}; + +export function getCloudTaskGatewayUrl(posthogHost: string): string { + const url = new URL(posthogHost); + let gatewayBaseUrl: string; + + if (url.hostname === "localhost" || url.hostname === "127.0.0.1") { + gatewayBaseUrl = `${url.protocol}//localhost:3308`; + } else if (url.hostname === "host.docker.internal") { + gatewayBaseUrl = `${url.protocol}//host.docker.internal:3308`; + } else if (url.hostname === "app.dev.posthog.dev") { + gatewayBaseUrl = "https://gateway.dev.posthog.dev"; + } else { + const region = url.hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us"; + gatewayBaseUrl = `https://gateway.${region}.posthog.com`; + } + + return `${gatewayBaseUrl}/posthog_code`; +} + +function isGatewayModel(value: unknown): value is Partial & { + id: string; +} { + return ( + typeof value === "object" && + value !== null && + typeof (value as { id?: unknown }).id === "string" + ); +} + +export function normalizeGatewayModelsResponse(value: unknown): GatewayModel[] { + const response = value as GatewayModelsResponse; + const entries = Array.isArray(value) + ? value + : Array.isArray(response?.data) + ? response.data + : Array.isArray(response?.models) + ? response.models + : []; + + return entries + .filter(isGatewayModel) + .filter((model) => !isBlockedModelId(model.id)) + .map((model) => ({ + id: model.id, + owned_by: model.owned_by ?? "", + context_window: Math.max( + model.context_window ?? 0, + MODEL_CONTEXT_WINDOW_OVERRIDES[model.id] ?? 0, + ), + supports_streaming: model.supports_streaming ?? false, + supports_vision: model.supports_vision ?? false, + allowed: model.allowed !== false, + restriction_reason: model.restriction_reason ?? null, + })); +} + +export function isBlockedModelId(modelId: string): boolean { + return BLOCKED_GATEWAY_MODELS.has(modelId.toLowerCase()); +} + +export function isAnthropicModel(model: GatewayModel): boolean { + if (model.owned_by) { + return model.owned_by === "anthropic"; + } + return model.id.startsWith("claude-") || model.id.startsWith("anthropic/"); +} + +export function isOpenAIModel(model: GatewayModel): boolean { + if (model.owned_by) { + return model.owned_by === "openai"; + } + return model.id.startsWith("gpt-") || model.id.startsWith("openai/"); +} + +export function isCloudflareModelId(modelId: string): boolean { + return modelId.startsWith("@cf/"); +} + +export function isGlmModelId(modelId: string): boolean { + return modelId.toLowerCase().includes("glm"); +} + +export function isCloudflareModel(model: GatewayModel): boolean { + return isCloudflareModelId(model.id) || model.owned_by === "cloudflare"; +} + +export function pickAllowedModel( + models: ReadonlyArray>, + preferred: string, +): string { + if (models.length === 0) return preferred; + const preferredEntry = models.find((model) => model.id === preferred); + if (!preferredEntry || preferredEntry.allowed) return preferred; + const allowed = models.filter((model) => model.allowed); + if (allowed.length === 0) return preferred; + return allowed.reduce((best, candidate) => + getClaudeModelRecency(candidate.id) >= getClaudeModelRecency(best.id) + ? candidate + : best, + ).id; +} + +export function getProviderName(ownedBy: string): string { + return PROVIDER_NAMES[ownedBy] ?? ownedBy; +} + +export function getClaudeModelRecency(modelId: string): number { + const match = modelId.toLowerCase().match(/-(\d+)(?:[-.](\d+))?/); + if (!match) return Number.MAX_SAFE_INTEGER; + const major = Number(match[1]); + const minor = match[2] ? Number(match[2]) : 0; + return major * 1000 + minor; +} + +function getModelFamilyRank(modelId: string): number { + const normalizedModelId = modelId.toLowerCase(); + const index = MODEL_FAMILY_ORDER.findIndex((family) => + normalizedModelId.includes(family), + ); + return index === -1 ? MODEL_FAMILY_ORDER.length : index; +} + +export function compareModelsForPicker(a: string, b: string): number { + const familyDiff = getModelFamilyRank(a) - getModelFamilyRank(b); + if (familyDiff !== 0) return familyDiff; + return getClaudeModelRecency(b) - getClaudeModelRecency(a); +} + +function stripProviderPrefix(modelId: string): string { + for (const prefix of PROVIDER_PREFIXES) { + if (modelId.startsWith(prefix)) { + return modelId.slice(prefix.length); + } + } + return modelId; +} + +function formatProviderModelName(modelId: string): string { + const [acronym, version, ...suffix] = modelId.split("-"); + if (!KNOWN_ACRONYMS.has(acronym.toLowerCase())) return modelId.toLowerCase(); + const head = version + ? `${acronym.toUpperCase()}-${version}` + : acronym.toUpperCase(); + const tail = suffix.map( + (word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(), + ); + return [head, ...tail].join(" "); +} + +export function formatGatewayModelName(model: GatewayModel): string { + if (isCloudflareModel(model)) { + return formatProviderModelName(model.id.split("/").pop() ?? model.id); + } + if (isOpenAIModel(model)) { + return formatProviderModelName(stripProviderPrefix(model.id)); + } + return formatModelId(model.id); +} + +export function formatModelId(modelId: string): string { + const cleanId = stripProviderPrefix(modelId).replace(/(\d)-(\d)/g, "$1.$2"); + return cleanId + .split(/[-_]/) + .map((word) => { + if (/^[0-9.]+$/.test(word)) return word; + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }) + .join(" "); +} + +function getAdapterModels( + models: readonly GatewayModel[], + adapter: Adapter, +): GatewayModel[] { + return models.filter((model) => + adapter === "codex" + ? isOpenAIModel(model) + : isAnthropicModel(model) || isCloudflareModel(model), + ); +} + +function getModeOptions( + adapter: Adapter, + modePresets?: readonly CloudTaskModePreset[], +): CloudTaskConfigSelectOption[] { + const modes = + modePresets ?? + (adapter === "codex" ? CODEX_MODE_PRESETS : CLAUDE_MODE_PRESETS); + return modes.map((mode) => ({ + value: mode.id, + name: mode.name, + description: mode.description, + })); +} + +export function buildCloudTaskConfigOptions( + models: readonly GatewayModel[], + adapter: Adapter, + modePresets?: readonly CloudTaskModePreset[], +): CloudTaskConfigOption[] { + const adapterModels = getAdapterModels(models, adapter); + const modelOptions: CloudTaskConfigSelectOption[] = adapterModels.map( + (model) => ({ + value: model.id, + name: formatGatewayModelName(model), + description: `Context: ${model.context_window.toLocaleString()} tokens`, + ...(model.allowed ? {} : { _meta: restrictedModelMeta() }), + }), + ); + + if (adapter === "claude") { + modelOptions.sort( + (a, b) => getClaudeModelRecency(a.value) - getClaudeModelRecency(b.value), + ); + } + + const defaultModel = + adapter === "codex" + ? (modelOptions.find((option) => option.value === DEFAULT_CODEX_MODEL) + ?.value ?? + modelOptions[0]?.value ?? + "") + : DEFAULT_GATEWAY_MODEL; + const preferredModelId = modelOptions.some( + (option) => option.value === defaultModel, + ) + ? defaultModel + : (modelOptions[0]?.value ?? defaultModel); + const resolvedModelId = pickAllowedModel(adapterModels, preferredModelId); + + if (!modelOptions.some((option) => option.value === resolvedModelId)) { + modelOptions.unshift({ + value: resolvedModelId, + name: resolvedModelId, + description: "Custom model", + }); + } + + const configOptions: CloudTaskConfigOption[] = [ + { + id: "mode", + name: "Approval Preset", + type: "select", + currentValue: adapter === "codex" ? "auto" : "plan", + options: getModeOptions(adapter, modePresets), + category: "mode", + description: "Choose an approval and sandboxing preset for your session", + }, + { + id: "model", + name: "Model", + type: "select", + currentValue: resolvedModelId, + options: modelOptions, + category: "model", + description: "Choose which model the agent should use", + }, + ]; + + const reasoningOptions = getReasoningEffortOptions(adapter, resolvedModelId); + if (reasoningOptions) { + configOptions.push({ + id: adapter === "codex" ? "reasoning_effort" : "effort", + name: adapter === "codex" ? "Reasoning Level" : "Effort", + type: "select", + currentValue: "high", + options: reasoningOptions, + category: "thought_level", + description: + adapter === "codex" + ? "Controls how much reasoning effort the model uses" + : "Controls how much effort Claude puts into its response", + }); + } + + return configOptions; +} diff --git a/packages/shared/src/domain-types.test.ts b/packages/shared/src/domain-types.test.ts index f9b060cbed..1264ae0462 100644 --- a/packages/shared/src/domain-types.test.ts +++ b/packages/shared/src/domain-types.test.ts @@ -1,5 +1,22 @@ import { describe, expect, it } from "vitest"; -import { isContentlessTask } from "./domain-types"; +import { + isContentlessTask, + isTerminalStatus, + TERMINAL_STATUSES, +} from "./domain-types"; + +describe("task run statuses", () => { + it.each(TERMINAL_STATUSES)("identifies %s as terminal", (status) => { + expect(isTerminalStatus(status)).toBe(true); + }); + + it.each(["not_started", "queued", "in_progress", "unknown", null, undefined])( + "identifies %s as non-terminal", + (status) => { + expect(isTerminalStatus(status)).toBe(false); + }, + ); +}); describe("isContentlessTask", () => { it.each([ diff --git a/packages/shared/src/domain-types.ts b/packages/shared/src/domain-types.ts index 4aa90fd2c7..3600423dae 100644 --- a/packages/shared/src/domain-types.ts +++ b/packages/shared/src/domain-types.ts @@ -4,6 +4,7 @@ import type { AgentRuntime } from "./agent-runtime"; import type { DismissalReasonOptionValue } from "./dismissal-reasons"; import type { StoredLogEntry } from "./session-events"; import type { TaskRunArtifact } from "./task"; +import type { UploadableSkillSource } from "./skills"; // Execution mode schema and type - shared between main and renderer export const executionModeSchema = z.enum([ @@ -192,6 +193,37 @@ export type TaskRunStatus = | "failed" | "cancelled"; +export type TaskRunEnvironment = "local" | "cloud"; + +export type ArtifactType = + | "plan" + | "context" + | "reference" + | "output" + | "artifact" + | "user_attachment" + | "skill_bundle"; + +export interface TaskRunArtifactMetadata { + skill_name: string; + skill_source: UploadableSkillSource; + content_sha256: string; + bundle_format: "zip"; + schema_version: number; +} + +export interface TaskRunArtifact { + id?: string; + name: string; + type: ArtifactType; + source?: string; + size?: number; + content_type?: string; + metadata?: TaskRunArtifactMetadata; + storage_path?: string; + uploaded_at?: string; +} + export const TERMINAL_STATUSES = ["completed", "failed", "cancelled"] as const; export function isTerminalStatus( @@ -220,7 +252,7 @@ export interface TaskRun { model?: string | null; reasoning_effort?: "low" | "medium" | "high" | "xhigh" | "max" | null; stage?: string | null; // Current stage (e.g., 'research', 'plan', 'build') - environment?: "local" | "cloud"; + environment?: TaskRunEnvironment; status: TaskRunStatus; log_url: string; error_message: string | null; diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index ff03602e7e..5809830aba 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -71,6 +71,30 @@ export { promptBlocksToText, serializeCloudPrompt, } from "./cloud-prompt"; +export { + BLOCKED_GATEWAY_MODEL_IDS, + buildCloudTaskConfigOptions, + type CloudTaskConfigOption, + type CloudTaskConfigSelectOption, + type CloudTaskModePreset, + compareModelsForPicker, + DEFAULT_CODEX_MODEL, + DEFAULT_GATEWAY_MODEL, + formatGatewayModelName, + formatModelId, + type GatewayModel, + getClaudeModelRecency, + getCloudTaskGatewayUrl, + getProviderName, + isAnthropicModel, + isBlockedModelId, + isCloudflareModel, + isCloudflareModelId, + isGlmModelId, + isOpenAIModel, + normalizeGatewayModelsResponse, + pickAllowedModel, +} from "./cloud-task-models"; export { buildInboxDeeplink, buildScoutDeeplink, @@ -87,9 +111,28 @@ export { export { DISMISSAL_REASON_OPTIONS, type DismissalReasonOptionValue, + dismissalReasonLabel, isDismissalReasonSnooze, } from "./dismissal-reasons"; -export type { SignalReportPriority, Task } from "./domain-types"; +export { + type ArtifactType, + type CloudPermissionOption, + type CloudTaskErrorUpdate, + type CloudTaskLogsUpdate, + type CloudTaskPermissionRequestUpdate, + type CloudTaskSnapshotUpdate, + type CloudTaskStatusUpdate, + type CloudTaskUpdatePayload, + isTerminalStatus, + type SignalReportPriority, + type Task, + type TaskRun, + type TaskRunArtifact, + type TaskRunArtifactMetadata, + type TaskRunEnvironment, + type TaskRunStatus, + TERMINAL_STATUSES, +} from "./domain-types"; export * from "./enrichment"; export { classifyGatewayLimitError, @@ -213,6 +256,13 @@ export { isPrivateIpv4Octets, isPrivateIpv6Literal, } from "./private-network"; +export { + DEFAULT_REASONING_EFFORT, + getReasoningEffortOptions, + isSupportedReasoningEffort, + type ReasoningEffortOption, + type SupportedReasoningEffort, +} from "./reasoning-effort"; export { type CloudRegion, formatRegionBadge, @@ -276,15 +326,19 @@ export { serializeSkillMarkdown, stripFrontmatter, } from "./skills"; -export type { - ArtifactType, - PostHogAPIConfig, - TaskRun, - TaskRunArtifact, - TaskRunArtifactMetadata, - TaskRunEnvironment, - TaskRunStatus, -} from "./task"; +export type { PostHogAPIConfig } from "./task"; +export { + type CreateTaskAutomationOptions, + createTaskAutomationSchema, + type TaskAutomation, + type TaskAutomationList, + type TaskAutomationValidationErrorDetails, + taskAutomationListSchema, + taskAutomationSchema, + taskAutomationValidationErrorSchema, + type UpdateTaskAutomationOptions, + updateTaskAutomationSchema, +} from "./task-automation"; export type { TaskCreationInput, TaskCreationOutput, diff --git a/packages/shared/src/reasoning-effort.test.ts b/packages/shared/src/reasoning-effort.test.ts new file mode 100644 index 0000000000..5b1e301dd3 --- /dev/null +++ b/packages/shared/src/reasoning-effort.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from "vitest"; +import { isSupportedReasoningEffort } from "./reasoning-effort"; + +describe("isSupportedReasoningEffort", () => { + it.each([ + ["codex", "gpt-5.5", "xhigh", true], + ["codex", "gpt-5.6-sol", "max", true], + ["codex", "gpt-5.4", "max", false], + ["claude", "claude-opus-4-8", "xhigh", true], + ["claude", "claude-sonnet-4-6", "xhigh", false], + ["claude", "claude-opus-4-8", "minimal", false], + ] as const)( + "validates %s %s effort %s", + (adapter, modelId, effort, expected) => { + expect(isSupportedReasoningEffort(adapter, modelId, effort)).toBe( + expected, + ); + }, + ); +}); diff --git a/packages/shared/src/reasoning-effort.ts b/packages/shared/src/reasoning-effort.ts new file mode 100644 index 0000000000..15f534a612 --- /dev/null +++ b/packages/shared/src/reasoning-effort.ts @@ -0,0 +1,77 @@ +import type { Adapter } from "./adapter"; + +export type SupportedReasoningEffort = + | "low" + | "medium" + | "high" + | "xhigh" + | "max"; + +export const DEFAULT_REASONING_EFFORT: SupportedReasoningEffort = "high"; + +export interface ReasoningEffortOption { + value: SupportedReasoningEffort; + name: string; +} + +const BASE_OPTIONS: ReasoningEffortOption[] = [ + { value: "low", name: "Low" }, + { value: "medium", name: "Medium" }, + { value: "high", name: "High" }, +]; + +const CLAUDE_MODELS_WITH_EFFORT = new Set([ + "claude-opus-4-7", + "claude-opus-4-8", + "claude-sonnet-4-6", + "claude-sonnet-5", + "claude-fable-5", +]); + +const CLAUDE_MODELS_WITH_XHIGH_EFFORT = new Set([ + "claude-opus-4-7", + "claude-opus-4-8", + "claude-sonnet-5", + "claude-fable-5", +]); + +export function getReasoningEffortOptions( + adapter: Adapter, + modelId: string, +): ReasoningEffortOption[] | null { + if (adapter === "claude" && !CLAUDE_MODELS_WITH_EFFORT.has(modelId)) { + return null; + } + + const options = [...BASE_OPTIONS]; + const normalizedModelId = modelId.toLowerCase(); + const supportsXhigh = + adapter === "claude" + ? CLAUDE_MODELS_WITH_XHIGH_EFFORT.has(modelId) + : normalizedModelId.includes("gpt-5.5") || + normalizedModelId.includes("gpt-5.6"); + + if (supportsXhigh) { + options.push({ value: "xhigh", name: "Extra High" }); + } + if ( + (adapter === "claude" && supportsXhigh) || + (adapter === "codex" && normalizedModelId.includes("gpt-5.6")) + ) { + options.push({ value: "max", name: "Max" }); + } + + return options; +} + +export function isSupportedReasoningEffort( + adapter: Adapter, + modelId: string, + value: string, +): value is SupportedReasoningEffort { + return ( + getReasoningEffortOptions(adapter, modelId)?.some( + (option) => option.value === value, + ) ?? false + ); +} diff --git a/packages/shared/src/sessions.ts b/packages/shared/src/sessions.ts index 76a49cd736..181ff36c20 100644 --- a/packages/shared/src/sessions.ts +++ b/packages/shared/src/sessions.ts @@ -8,9 +8,9 @@ import type { } from "@agentclientprotocol/sdk"; import type { Adapter } from "./adapter"; import type { SkillButtonId } from "./analytics-events"; +import type { TaskRunArtifact, TaskRunStatus } from "./domain-types"; import type { ExecutionMode } from "./exec-types"; import type { AcpMessage } from "./session-events"; -import type { TaskRunArtifact, TaskRunStatus } from "./task"; export type { Adapter }; diff --git a/packages/shared/src/task-automation.test.ts b/packages/shared/src/task-automation.test.ts new file mode 100644 index 0000000000..57e6fcaa97 --- /dev/null +++ b/packages/shared/src/task-automation.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, expectTypeOf, it } from "vitest"; +import { + type CreateTaskAutomationOptions, + createTaskAutomationSchema, + taskAutomationSchema, + taskAutomationValidationErrorSchema, + type UpdateTaskAutomationOptions, + updateTaskAutomationSchema, +} from "./task-automation"; + +describe("task automation contracts", () => { + it("normalizes optional automation response fields", () => { + expect( + taskAutomationSchema.parse({ + id: "automation-1", + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + cron_expression: "0 9 * * *", + last_run_at: null, + last_run_status: null, + last_task_id: null, + last_task_run_id: null, + last_error: null, + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:00:00Z", + }), + ).toMatchObject({ + github_integration: null, + timezone: null, + template_id: null, + enabled: true, + }); + }); + + it("keeps create fields required and update fields partial", () => { + const create = createTaskAutomationSchema.parse({ + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + cron_expression: "0 9 * * *", + timezone: "Europe/London", + }); + const update = updateTaskAutomationSchema.parse({ enabled: false }); + + expect(create.timezone).toBe("Europe/London"); + expect(update).toEqual({ enabled: false }); + expectTypeOf(create).toEqualTypeOf(); + expectTypeOf(update).toEqualTypeOf(); + }); + + it("preserves backend validation field attribution", () => { + expect( + taskAutomationValidationErrorSchema.parse({ + type: "validation_error", + detail: "Enter a valid cron expression.", + attr: "cron_expression", + }), + ).toEqual({ + type: "validation_error", + code: "invalid_input", + detail: "Enter a valid cron expression.", + attr: "cron_expression", + }); + }); +}); diff --git a/packages/shared/src/task-automation.ts b/packages/shared/src/task-automation.ts new file mode 100644 index 0000000000..0f3e4a4bde --- /dev/null +++ b/packages/shared/src/task-automation.ts @@ -0,0 +1,58 @@ +import { z } from "zod"; + +export const taskAutomationSchema = z.object({ + id: z.string(), + name: z.string(), + prompt: z.string(), + repository: z.string(), + github_integration: z.number().nullable().default(null), + cron_expression: z.string(), + timezone: z.string().nullable().default(null), + template_id: z.string().nullable().default(null), + enabled: z.boolean().default(true), + last_run_at: z.string().nullable(), + last_run_status: z.string().nullable(), + last_task_id: z.string().nullable(), + last_task_run_id: z.string().nullable(), + last_error: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), +}); +export type TaskAutomation = z.infer; + +export const taskAutomationListSchema = z.object({ + count: z.number(), + next: z.string().nullable().optional(), + previous: z.string().nullable().optional(), + results: z.array(taskAutomationSchema), +}); +export type TaskAutomationList = z.infer; + +export const createTaskAutomationSchema = z.object({ + name: z.string(), + prompt: z.string(), + repository: z.string(), + github_integration: z.number().nullable().optional(), + cron_expression: z.string(), + timezone: z.string(), + template_id: z.string().nullable().optional(), + enabled: z.boolean().optional(), +}); +export type CreateTaskAutomationOptions = z.infer< + typeof createTaskAutomationSchema +>; + +export const updateTaskAutomationSchema = createTaskAutomationSchema.partial(); +export type UpdateTaskAutomationOptions = z.infer< + typeof updateTaskAutomationSchema +>; + +export const taskAutomationValidationErrorSchema = z.object({ + type: z.string().optional(), + code: z.string().default("invalid_input"), + detail: z.string(), + attr: z.string().nullable().default(null), +}); +export type TaskAutomationValidationErrorDetails = z.infer< + typeof taskAutomationValidationErrorSchema +>; diff --git a/packages/shared/src/task.test.ts b/packages/shared/src/task.test.ts new file mode 100644 index 0000000000..8d666f2b11 --- /dev/null +++ b/packages/shared/src/task.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, expectTypeOf, it } from "vitest"; +import type { + Task, + TaskRun, + TaskRunArtifact, + TaskRunStatus, +} from "./domain-types"; +import { + type CloudPermissionOption, + type CloudTaskUpdatePayload, + isTerminalStatus, + type Task as RootTask, + type TaskRun as RootTaskRun, + type TaskRunArtifact as RootTaskRunArtifact, + type TaskRunStatus as RootTaskRunStatus, + TERMINAL_STATUSES, +} from "./index"; +import type { + Task as LegacyTask, + TaskRun as LegacyTaskRun, + TaskRunArtifact as LegacyTaskRunArtifact, + TaskRunStatus as LegacyTaskRunStatus, +} from "./task"; + +describe("cloud task contract exports", () => { + it("keeps legacy and root task exports canonical", () => { + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); + }); + + it("exports cloud permission and update payload contracts from the root", () => { + expectTypeOf().toMatchTypeOf<{ + kind: string; + optionId: string; + name: string; + }>(); + expectTypeOf().toEqualTypeOf< + "logs" | "status" | "snapshot" | "error" | "permission_request" + >(); + }); + + it("exports terminal status helpers from the root", () => { + expect(TERMINAL_STATUSES).toEqual(["completed", "failed", "cancelled"]); + expect(isTerminalStatus("completed")).toBe(true); + expect(isTerminalStatus("in_progress")).toBe(false); + }); +}); diff --git a/packages/shared/src/task.ts b/packages/shared/src/task.ts index b93c6e96d7..f6593d21bf 100644 --- a/packages/shared/src/task.ts +++ b/packages/shared/src/task.ts @@ -1,97 +1,12 @@ -// PostHog Task model (matches the desktop task API's OpenAPI schema) -import type { AgentRuntime } from "./agent-runtime"; -import type { UploadableSkillSource } from "./skills"; - -export interface Task { - id: string; - task_number?: number; - slug?: string; - title: string; - description: string; - origin_product: - | "error_tracking" - | "eval_clusters" - | "user_created" - | "support_queue" - | "session_summaries" - | "signal_report" - | "signals_scout" - | "slack"; - signal_report?: string | null; // Inbox report UUID when origin_product is "signal_report" - github_integration?: number | null; - repository: string; // Format: "organization/repository" (e.g., "posthog/posthog-js") - json_schema?: Record | null; // JSON schema for task output validation - internal?: boolean; - runtime?: AgentRuntime; - created_at: string; - updated_at: string; - created_by?: { - id: number; - uuid: string; - distinct_id: string; - first_name: string; - email: string; - }; - latest_run?: TaskRun; -} - -export type ArtifactType = - | "plan" - | "context" - | "reference" - | "output" - | "artifact" - | "user_attachment" - | "skill_bundle"; - -export interface TaskRunArtifactMetadata { - skill_name: string; - skill_source: UploadableSkillSource; - content_sha256: string; - bundle_format: "zip"; - schema_version: number; -} - -export interface TaskRunArtifact { - id?: string; - name: string; - type: ArtifactType; - source?: string; - size?: number; - content_type?: string; - metadata?: TaskRunArtifactMetadata; - storage_path?: string; - uploaded_at?: string; -} - -export type TaskRunStatus = - | "not_started" - | "queued" - | "in_progress" - | "completed" - | "failed" - | "cancelled"; - -export type TaskRunEnvironment = "local" | "cloud"; - -// TaskRun model - represents individual execution runs of tasks -export interface TaskRun { - id: string; - task: string; // Task ID - team: number; - branch: string | null; - stage: string | null; // Current stage (e.g., 'research', 'plan', 'build') - environment: TaskRunEnvironment; - status: TaskRunStatus; - log_url: string; - error_message: string | null; - output: Record | null; // Structured output (PR URL, commit SHA, etc.) - state: Record; // Intermediate run state (defaults to {}, never null) - artifacts?: TaskRunArtifact[]; - created_at: string; - updated_at: string; - completed_at: string | null; -} +export type { + ArtifactType, + Task, + TaskRun, + TaskRunArtifact, + TaskRunArtifactMetadata, + TaskRunEnvironment, + TaskRunStatus, +} from "./domain-types"; export interface PostHogAPIConfig { apiUrl: string; diff --git a/packages/workspace-server/src/services/agent/agent.ts b/packages/workspace-server/src/services/agent/agent.ts index 9654cb30b7..bd0821e254 100644 --- a/packages/workspace-server/src/services/agent/agent.ts +++ b/packages/workspace-server/src/services/agent/agent.ts @@ -20,24 +20,16 @@ import { } from "@posthog/agent"; import type { McpToolApprovals } from "@posthog/agent/adapters/claude/mcp/tool-metadata"; import { hydrateSessionJsonl } from "@posthog/agent/adapters/claude/session/jsonl-hydration"; -import { getReasoningEffortOptions } from "@posthog/agent/adapters/reasoning-effort"; import { Agent } from "@posthog/agent/agent"; import { getAvailableCodexModes, getAvailableModes, } from "@posthog/agent/execution-mode"; import { - DEFAULT_CODEX_MODEL, - DEFAULT_GATEWAY_MODEL, fetchGatewayModels, formatGatewayModelName, - type GatewayModel, getClaudeModelRecency, getProviderName, - isAnthropicModel, - isCloudflareModel, - isOpenAIModel, - pickAllowedModel, } from "@posthog/agent/gateway-models"; import { getLlmGatewayUrl } from "@posthog/agent/posthog-api"; import { @@ -72,10 +64,10 @@ import { import { type AcpMessage, type Adapter, + buildCloudTaskConfigOptions, type ExecutionMode, isAuthError, resolveCloudInitialPermissionMode, - restrictedModelMeta, serializeError, TypedEventEmitter, } from "@posthog/shared"; @@ -2395,111 +2387,14 @@ For git operations while detached: adapter: Adapter = "claude", ): Promise { const gatewayUrl = getLlmGatewayUrl(apiHost); - // Authenticated so the gateway can mark plan-restricted models; falls - // back to an anonymous fetch (everything allowed) without auth. const gatewayModels = await fetchGatewayModels({ gatewayUrl, authToken: (await this.agentAuthAdapter.gatewayAuthToken()) ?? undefined, }); - - // The Claude adapter can also drive Cloudflare `@cf/` models the gateway serves over its - // Anthropic-Messages surface, so the preview/default-model path must offer them too — otherwise an - // advertised `@cf/*` model is dropped here and the pre-session run falls back to Opus. - const modelFilter = - adapter === "codex" - ? isOpenAIModel - : (model: GatewayModel) => - isAnthropicModel(model) || isCloudflareModel(model); - - const adapterModels = gatewayModels.filter((model) => modelFilter(model)); - const modelOptions = adapterModels.map((model) => ({ - value: model.id, - name: formatGatewayModelName(model), - description: `Context: ${model.context_window.toLocaleString()} tokens`, - // Locked models stay listed so the picker can gate them instead of - // silently dropping them. - ...(model.allowed ? {} : { _meta: restrictedModelMeta() }), - })); - - // The gateway returns models in an arbitrary order. Sort Claude models - // oldest-to-newest so the picker is deterministic and the newest model - // lands at the end of the list, closest to the trigger. - if (adapter === "claude") { - modelOptions.sort( - (a, b) => - getClaudeModelRecency(a.value) - getClaudeModelRecency(b.value), - ); - } - - const defaultModel = - adapter === "codex" - ? (modelOptions.find((o) => o.value === DEFAULT_CODEX_MODEL)?.value ?? - modelOptions[0]?.value ?? - "") - : DEFAULT_GATEWAY_MODEL; - - const preferredModelId = modelOptions.some((o) => o.value === defaultModel) - ? defaultModel - : (modelOptions[0]?.value ?? defaultModel); - // Never preselect a model the org's plan can't use — it would 403 on the - // first message. - const resolvedModelId = pickAllowedModel(adapterModels, preferredModelId); - - if (!modelOptions.some((o) => o.value === resolvedModelId)) { - modelOptions.unshift({ - value: resolvedModelId, - name: resolvedModelId, - description: "Custom model", - }); - } - - const modes = - adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(); - const modeOptions = modes.map((mode) => ({ - value: mode.id, - name: mode.name, - description: mode.description ?? undefined, - })); - const defaultMode = adapter === "codex" ? "auto" : "plan"; - - const configOptions: SessionConfigOption[] = [ - { - id: "mode", - name: "Approval Preset", - type: "select", - currentValue: defaultMode, - options: modeOptions, - category: "mode", - description: - "Choose an approval and sandboxing preset for your session", - }, - { - id: "model", - name: "Model", - type: "select", - currentValue: resolvedModelId, - options: modelOptions, - category: "model", - description: "Choose which model Claude should use", - }, - ]; - - const effortOpts = getReasoningEffortOptions(adapter, resolvedModelId); - if (effortOpts) { - configOptions.push({ - id: adapter === "codex" ? "reasoning_effort" : "effort", - name: adapter === "codex" ? "Reasoning Level" : "Effort", - type: "select", - currentValue: "high", - options: effortOpts, - category: "thought_level", - description: - adapter === "codex" - ? "Controls how much reasoning effort the model uses" - : "Controls how much effort Claude puts into its response", - }); - } - - return configOptions; + return buildCloudTaskConfigOptions( + gatewayModels, + adapter, + adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(), + ) as SessionConfigOption[]; } } From ec5fb80c4e293a8a3f29a5bbfadb1559320cfb6c Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:45:36 +0300 Subject: [PATCH 02/42] fix(models): preserve GLM effort policy Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/shared/src/cloud-task-models.test.ts | 11 ++++- packages/shared/src/reasoning-effort.test.ts | 3 ++ packages/shared/src/reasoning-effort.ts | 48 ++++++++++--------- 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/packages/shared/src/cloud-task-models.test.ts b/packages/shared/src/cloud-task-models.test.ts index 31418a7775..afbad989f8 100644 --- a/packages/shared/src/cloud-task-models.test.ts +++ b/packages/shared/src/cloud-task-models.test.ts @@ -164,8 +164,17 @@ describe("buildCloudTaskConfigOptions", () => { { value: "@cf/zai-org/glm-5.2" }, ], }, + { + id: "effort", + currentValue: "high", + options: [{ value: "high" }, { value: "max" }], + }, + ]); + expect(options.map((option) => option.id)).toEqual([ + "mode", + "model", + "effort", ]); - expect(options.map((option) => option.id)).toEqual(["mode", "model"]); }); it("builds Codex options with the shared default and reasoning levels", () => { diff --git a/packages/shared/src/reasoning-effort.test.ts b/packages/shared/src/reasoning-effort.test.ts index 5b1e301dd3..2ac4a9bbff 100644 --- a/packages/shared/src/reasoning-effort.test.ts +++ b/packages/shared/src/reasoning-effort.test.ts @@ -8,6 +8,9 @@ describe("isSupportedReasoningEffort", () => { ["codex", "gpt-5.4", "max", false], ["claude", "claude-opus-4-8", "xhigh", true], ["claude", "claude-sonnet-4-6", "xhigh", false], + ["claude", "@cf/zai-org/glm-5.2", "high", true], + ["claude", "@cf/zai-org/glm-5.2", "max", true], + ["claude", "@cf/zai-org/glm-5.2", "medium", false], ["claude", "claude-opus-4-8", "minimal", false], ] as const)( "validates %s %s effort %s", diff --git a/packages/shared/src/reasoning-effort.ts b/packages/shared/src/reasoning-effort.ts index 15f534a612..2fe12b7232 100644 --- a/packages/shared/src/reasoning-effort.ts +++ b/packages/shared/src/reasoning-effort.ts @@ -20,44 +20,46 @@ const BASE_OPTIONS: ReasoningEffortOption[] = [ { value: "high", name: "High" }, ]; -const CLAUDE_MODELS_WITH_EFFORT = new Set([ - "claude-opus-4-7", - "claude-opus-4-8", - "claude-sonnet-4-6", - "claude-sonnet-5", - "claude-fable-5", -]); +const CLAUDE_MODEL_EFFORTS: Readonly< + Record +> = { + "claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"], + "claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"], + "claude-sonnet-4-6": ["low", "medium", "high"], + "claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"], + "claude-fable-5": ["low", "medium", "high", "xhigh", "max"], + "@cf/zai-org/glm-5.2": ["high", "max"], +}; -const CLAUDE_MODELS_WITH_XHIGH_EFFORT = new Set([ - "claude-opus-4-7", - "claude-opus-4-8", - "claude-sonnet-5", - "claude-fable-5", -]); +const EFFORT_NAMES: Record = { + low: "Low", + medium: "Medium", + high: "High", + xhigh: "Extra High", + max: "Max", +}; export function getReasoningEffortOptions( adapter: Adapter, modelId: string, ): ReasoningEffortOption[] | null { - if (adapter === "claude" && !CLAUDE_MODELS_WITH_EFFORT.has(modelId)) { - return null; + if (adapter === "claude") { + const efforts = CLAUDE_MODEL_EFFORTS[modelId]; + return ( + efforts?.map((value) => ({ value, name: EFFORT_NAMES[value] })) ?? null + ); } const options = [...BASE_OPTIONS]; const normalizedModelId = modelId.toLowerCase(); const supportsXhigh = - adapter === "claude" - ? CLAUDE_MODELS_WITH_XHIGH_EFFORT.has(modelId) - : normalizedModelId.includes("gpt-5.5") || - normalizedModelId.includes("gpt-5.6"); + normalizedModelId.includes("gpt-5.5") || + normalizedModelId.includes("gpt-5.6"); if (supportsXhigh) { options.push({ value: "xhigh", name: "Extra High" }); } - if ( - (adapter === "claude" && supportsXhigh) || - (adapter === "codex" && normalizedModelId.includes("gpt-5.6")) - ) { + if (adapter === "codex" && normalizedModelId.includes("gpt-5.6")) { options.push({ value: "max", name: "Max" }); } From fa2dda48da93a111d7ddca4081ce3763433da716 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 01:55:53 +0300 Subject: [PATCH 03/42] test(ui): wait for async content Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../components/session-update/PlanApprovalView.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx index ef705ba77c..dd83ad5f6b 100644 --- a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx +++ b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx @@ -110,7 +110,7 @@ describe("PlanApprovalView", () => { ).toBeInTheDocument(); }); - it("uses updated content instead of stale raw input while streaming", () => { + it("uses updated content instead of stale raw input while streaming", async () => { renderView({ toolCall: makeToolCall({ status: "in_progress", @@ -124,7 +124,7 @@ describe("PlanApprovalView", () => { }), }); - expect(screen.getByText("Updated plan")).toBeInTheDocument(); + expect(await screen.findByText("Updated plan")).toBeInTheDocument(); expect(screen.queryByText("Initial plan")).not.toBeInTheDocument(); }); From 0960bc378d53c5e5e3cdb261eb619953bb69f18a Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 02:06:34 +0300 Subject: [PATCH 04/42] test(ui): isolate plan approval presentation Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../components/session-update/PlanApprovalView.test.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx index dd83ad5f6b..3bd1add8e8 100644 --- a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx +++ b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.test.tsx @@ -2,9 +2,13 @@ import type { ToolCall } from "@posthog/ui/features/sessions/types"; import { Theme } from "@radix-ui/themes"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { PlanApprovalView } from "./PlanApprovalView"; +vi.mock("../../../permissions/PlanContent", () => ({ + PlanContent: ({ plan }: { plan: string }) =>
{plan}
, +})); + const PLAN_MARKER = "Sentinel plan body for testing"; function makeToolCall(overrides: Partial = {}): ToolCall { @@ -124,7 +128,7 @@ describe("PlanApprovalView", () => { }), }); - expect(await screen.findByText("Updated plan")).toBeInTheDocument(); + expect(screen.getByText("Updated plan")).toBeInTheDocument(); expect(screen.queryByText("Initial plan")).not.toBeInTheDocument(); }); From 3f608f21ce8b2a1cf0a4277ed6fd77832371ee8f Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Tue, 28 Jul 2026 12:05:07 +0300 Subject: [PATCH 05/42] fix(shared): Preserve canonical task artifacts Keep the extracted domain declaration as the single source after rebasing onto current main. Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/shared/src/domain-types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared/src/domain-types.ts b/packages/shared/src/domain-types.ts index 3600423dae..8f1e8dc56d 100644 --- a/packages/shared/src/domain-types.ts +++ b/packages/shared/src/domain-types.ts @@ -3,7 +3,6 @@ import type { Adapter } from "./adapter"; import type { AgentRuntime } from "./agent-runtime"; import type { DismissalReasonOptionValue } from "./dismissal-reasons"; import type { StoredLogEntry } from "./session-events"; -import type { TaskRunArtifact } from "./task"; import type { UploadableSkillSource } from "./skills"; // Execution mode schema and type - shared between main and renderer From 9c4235c3f9f0b9ca26c0eb2aac4f547288b57029 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Tue, 28 Jul 2026 12:27:36 +0300 Subject: [PATCH 06/42] test(agent): match canonical model picker order Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/agent/src/adapters/claude/session/model-config.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/agent/src/adapters/claude/session/model-config.test.ts b/packages/agent/src/adapters/claude/session/model-config.test.ts index 22fd0bcf69..5561454baa 100644 --- a/packages/agent/src/adapters/claude/session/model-config.test.ts +++ b/packages/agent/src/adapters/claude/session/model-config.test.ts @@ -26,8 +26,8 @@ describe("applyAvailableModelsAllowlist", () => { "claude-opus-4-8", ]).options, ).toEqual([ - { value: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, { value: "claude-opus-4-8", name: "Claude Opus 4.8" }, + { value: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" }, ]); }); From b69d17d464d63c87e1b054625fbd1fd4b16cc9f6 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:18:07 +0300 Subject: [PATCH 07/42] refactor(api-client): extract cloud task transport Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/api-client/package.json | 1 - packages/api-client/src/fetcher.test.ts | 39 ++ packages/api-client/src/fetcher.ts | 25 +- packages/api-client/src/index.ts | 6 +- .../src/posthog-client.automations.test.ts | 192 +++++++++ .../api-client/src/posthog-client.test.ts | 349 ++++++++++++++++- packages/api-client/src/posthog-client.ts | 368 +++++++++++++++--- .../api-client/src/task-normalization.test.ts | 115 ++++++ packages/api-client/src/task-normalization.ts | 203 ++++++++++ pnpm-lock.yaml | 12 +- 10 files changed, 1244 insertions(+), 66 deletions(-) create mode 100644 packages/api-client/src/posthog-client.automations.test.ts create mode 100644 packages/api-client/src/task-normalization.test.ts create mode 100644 packages/api-client/src/task-normalization.ts diff --git a/packages/api-client/package.json b/packages/api-client/package.json index bee0ebc547..3a592020a4 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -25,7 +25,6 @@ "src/**/*" ], "dependencies": { - "@posthog/agent": "workspace:*", "@posthog/shared": "workspace:*" } } diff --git a/packages/api-client/src/fetcher.test.ts b/packages/api-client/src/fetcher.test.ts index c205949418..6e1e17a351 100644 --- a/packages/api-client/src/fetcher.test.ts +++ b/packages/api-client/src/fetcher.test.ts @@ -53,6 +53,45 @@ describe("buildApiFetcher", () => { expect(mockFetch.mock.calls[0][1].headers.get("Authorization")).toBe( "Bearer my-token", ); + expect(mockFetch.mock.calls[0][1].headers.get("User-Agent")).toBe( + "posthog/desktop.hog.dev; version: test", + ); + }); + + it("uses an injected fetch implementation and custom user agent", async () => { + const injectedFetch = vi.fn().mockResolvedValueOnce(ok()); + const fetcher = buildApiFetcher({ + getAccessToken: vi.fn().mockResolvedValue("token"), + refreshAccessToken: vi.fn().mockResolvedValue("new-token"), + appVersion: "1.2.3", + fetch: injectedFetch, + userAgent: "posthog/mobile; version: 1.2.3", + }); + + await fetcher.fetch(mockInput); + + expect(injectedFetch).toHaveBeenCalledTimes(1); + expect(mockFetch).not.toHaveBeenCalled(); + expect(injectedFetch.mock.calls[0][1].headers.get("User-Agent")).toBe( + "posthog/mobile; version: 1.2.3", + ); + }); + + it("omits the user agent when explicitly disabled", async () => { + const injectedFetch = vi.fn().mockResolvedValueOnce(ok()); + const fetcher = buildApiFetcher({ + getAccessToken: vi.fn().mockResolvedValue("token"), + refreshAccessToken: vi.fn().mockResolvedValue("new-token"), + appVersion: "1.2.3", + fetch: injectedFetch, + userAgent: null, + }); + + await fetcher.fetch(mockInput); + + expect(injectedFetch.mock.calls[0][1].headers.has("User-Agent")).toBe( + false, + ); }); it("retries once with a freshly fetched token on 401", async () => { diff --git a/packages/api-client/src/fetcher.ts b/packages/api-client/src/fetcher.ts index 6bf59aa9f8..bc061a3030 100644 --- a/packages/api-client/src/fetcher.ts +++ b/packages/api-client/src/fetcher.ts @@ -1,9 +1,16 @@ import type { createApiClient } from "./generated"; +export type FetchImplementation = ( + input: string | URL | Request, + init?: RequestInit, +) => Promise; + export type ApiFetcherConfig = { getAccessToken: () => Promise; refreshAccessToken: () => Promise; appVersion: string; + fetch?: FetchImplementation; + userAgent?: string | null; }; /** @@ -13,11 +20,13 @@ export type ApiFetcherConfig = { */ export class ApiRequestError extends Error { readonly status: number; + readonly body: unknown; - constructor(status: number, serializedBody: string) { + constructor(status: number, serializedBody: string, body?: unknown) { super(`Failed request: [${status}] ${serializedBody}`); this.name = "ApiRequestError"; this.status = status; + this.body = body; } } @@ -29,7 +38,11 @@ export function requestErrorStatus(error: unknown): number | undefined { export const buildApiFetcher: ( config: ApiFetcherConfig, ) => Parameters[0] = (config) => { - const userAgent = `posthog/desktop.hog.dev; version: ${config.appVersion}`; + const fetchImpl = config.fetch ?? globalThis.fetch; + const userAgent = + config.userAgent === undefined + ? `posthog/desktop.hog.dev; version: ${config.appVersion}` + : config.userAgent; const makeRequest = async ( input: Parameters[0]["fetch"]>[0], @@ -38,7 +51,9 @@ export const buildApiFetcher: ( const headers = new Headers(); headers.set("Authorization", `Bearer ${token}`); headers.set("Content-Type", "application/json"); - headers.set("User-Agent", userAgent); + if (userAgent) { + headers.set("User-Agent", userAgent); + } if (input.urlSearchParams) { input.url.search = input.urlSearchParams.toString(); @@ -59,7 +74,7 @@ export const buildApiFetcher: ( } try { - const response = await fetch(input.url, { + const response = await fetchImpl(input.url, { method: input.method.toUpperCase(), ...(body && { body }), headers, @@ -114,6 +129,7 @@ export const buildApiFetcher: ( throw new ApiRequestError( response.status, JSON.stringify(errorResponse), + errorResponse, ); } } @@ -128,6 +144,7 @@ export const buildApiFetcher: ( throw new ApiRequestError( response.status, JSON.stringify(errorResponse), + errorResponse, ); } diff --git a/packages/api-client/src/index.ts b/packages/api-client/src/index.ts index e6b7c3c639..a18d6ff4b0 100644 --- a/packages/api-client/src/index.ts +++ b/packages/api-client/src/index.ts @@ -1,6 +1,10 @@ import "./generated.augment"; -export { type ApiFetcherConfig, buildApiFetcher } from "./fetcher"; +export { + type ApiFetcherConfig, + buildApiFetcher, + type FetchImplementation, +} from "./fetcher"; export { createApiClient, type Schemas } from "./generated"; export { createLoop, diff --git a/packages/api-client/src/posthog-client.automations.test.ts b/packages/api-client/src/posthog-client.automations.test.ts new file mode 100644 index 0000000000..3d3386ef3a --- /dev/null +++ b/packages/api-client/src/posthog-client.automations.test.ts @@ -0,0 +1,192 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + PostHogAPIClient, + TaskAutomationValidationError, +} from "./posthog-client"; + +const automationPayload = { + id: "automation-1", + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + github_integration: 7, + cron_expression: "0 9 * * *", + timezone: "Europe/London", + template_id: "llm-skill:daily-prs", + enabled: true, + last_run_at: null, + last_run_status: null, + last_task_id: null, + last_task_run_id: null, + last_error: null, + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:00:00Z", +}; + +function jsonResponse(body: unknown, status = 200): Response { + return new Response(JSON.stringify(body), { + status, + headers: { "Content-Type": "application/json" }, + }); +} + +describe("PostHogAPIClient task automations", () => { + const fetch = vi.fn(); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "access-token", + async () => "refreshed-token", + 42, + { appVersion: "test", fetch }, + ); + + beforeEach(() => { + fetch.mockReset(); + }); + + it("lists automations and normalizes optional response fields", async () => { + const minimalPayload = { + ...automationPayload, + github_integration: undefined, + timezone: undefined, + template_id: undefined, + enabled: undefined, + }; + fetch.mockResolvedValueOnce( + jsonResponse({ + count: 1, + next: null, + previous: null, + results: [minimalPayload], + }), + ); + + await expect(client.listTaskAutomations()).resolves.toEqual([ + expect.objectContaining({ + id: "automation-1", + github_integration: null, + timezone: null, + template_id: null, + enabled: true, + }), + ]); + expect(fetch).toHaveBeenCalledWith( + new URL( + "https://app.posthog.test/api/projects/42/task_automations/?limit=500", + ), + expect.objectContaining({ method: "GET" }), + ); + }); + + it("gets and creates automations through generated endpoints", async () => { + fetch + .mockResolvedValueOnce(jsonResponse(automationPayload)) + .mockResolvedValueOnce(jsonResponse(automationPayload, 201)); + + await expect(client.getTaskAutomation("automation-1")).resolves.toEqual( + automationPayload, + ); + await expect( + client.createTaskAutomation({ + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + github_integration: 7, + cron_expression: "0 9 * * *", + timezone: "Europe/London", + template_id: "llm-skill:daily-prs", + enabled: true, + }), + ).resolves.toEqual(automationPayload); + + expect(fetch).toHaveBeenNthCalledWith( + 2, + new URL("https://app.posthog.test/api/projects/42/task_automations/"), + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + github_integration: 7, + cron_expression: "0 9 * * *", + timezone: "Europe/London", + template_id: "llm-skill:daily-prs", + enabled: true, + }), + }), + ); + }); + + it("updates, deletes, and runs automations", async () => { + fetch + .mockResolvedValueOnce( + jsonResponse({ ...automationPayload, enabled: false }), + ) + .mockResolvedValueOnce(new Response(null, { status: 204 })) + .mockResolvedValueOnce(jsonResponse(automationPayload)); + + await expect( + client.updateTaskAutomation("automation-1", { enabled: false }), + ).resolves.toMatchObject({ enabled: false }); + await expect( + client.deleteTaskAutomation("automation-1"), + ).resolves.toBeUndefined(); + await expect(client.runTaskAutomation("automation-1")).resolves.toEqual( + automationPayload, + ); + + expect(fetch).toHaveBeenNthCalledWith( + 1, + new URL( + "https://app.posthog.test/api/projects/42/task_automations/automation-1/", + ), + expect.objectContaining({ + method: "PATCH", + body: JSON.stringify({ enabled: false }), + }), + ); + expect(fetch).toHaveBeenNthCalledWith( + 3, + new URL( + "https://app.posthog.test/api/projects/42/task_automations/automation-1/run/", + ), + expect.objectContaining({ method: "POST" }), + ); + expect(fetch.mock.calls[2]?.[1]?.body).toBeUndefined(); + }); + + it("preserves validation detail, code, and field attribution", async () => { + fetch.mockResolvedValueOnce( + new Response( + JSON.stringify({ + type: "validation_error", + code: "invalid_input", + detail: "Enter a valid cron expression.", + attr: "cron_expression", + }), + { + status: 400, + statusText: "Bad Request", + headers: { "Content-Type": "application/json" }, + }, + ), + ); + + const request = client.createTaskAutomation({ + name: "Daily PRs", + prompt: "Check PRs", + repository: "posthog/posthog", + cron_expression: "not a cron", + timezone: "Europe/London", + }); + + await expect(request).rejects.toBeInstanceOf(TaskAutomationValidationError); + await expect(request).rejects.toMatchObject({ + status: 400, + code: "invalid_input", + attr: "cron_expression", + message: "Enter a valid cron expression.", + }); + }); +}); diff --git a/packages/api-client/src/posthog-client.test.ts b/packages/api-client/src/posthog-client.test.ts index 42a36681cb..da93bc7960 100644 --- a/packages/api-client/src/posthog-client.test.ts +++ b/packages/api-client/src/posthog-client.test.ts @@ -1,8 +1,337 @@ import { describe, expect, it, vi } from "vitest"; import { ApiRequestError } from "./fetcher"; -import { PostHogAPIClient } from "./posthog-client"; +import { CloudCommandError, PostHogAPIClient } from "./posthog-client"; describe("PostHogAPIClient", () => { + it.each([ + "user_message", + "permission_response", + "set_config_option", + "cancel", + ] as const)("sends the %s cloud run command", async (method) => { + const fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ result: { accepted: true } }), { + status: 200, + }), + ); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + await expect( + client.sendCloudRunCommand("task-1", "run-1", method, { + value: "payload", + }), + ).resolves.toEqual({ accepted: true }); + + expect(fetch).toHaveBeenCalledWith( + new URL( + "https://app.posthog.test/api/projects/42/tasks/task-1/runs/run-1/command/", + ), + expect.objectContaining({ + method: "POST", + body: expect.any(String), + }), + ); + const request = fetch.mock.calls[0][1] as RequestInit; + expect(JSON.parse(request.body as string)).toMatchObject({ + jsonrpc: "2.0", + method, + params: { value: "payload" }, + }); + }); + + it("throws structured cloud command errors for HTTP failures", async () => { + const fetch = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ error: "No active sandbox for this run" }), + { + status: 409, + statusText: "Conflict", + }, + ), + ); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + const error = await client + .sendCloudRunCommand("task-1", "run-1", "user_message") + .catch((caught: unknown) => caught); + + expect(error).toMatchObject({ + name: "CloudCommandError", + method: "user_message", + status: 409, + backendError: "No active sandbox for this run", + }); + expect(error).toBeInstanceOf(CloudCommandError); + expect((error as CloudCommandError).isSandboxInactive()).toBe(true); + }); + + it("throws structured cloud command errors for JSON-RPC failures", async () => { + const fetch = vi + .fn() + .mockResolvedValue( + new Response( + JSON.stringify({ error: { message: "Permission request expired" } }), + { status: 200 }, + ), + ); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + await expect( + client.sendCloudRunCommand("task-1", "run-1", "permission_response"), + ).rejects.toMatchObject({ + method: "permission_response", + status: 200, + backendError: "Permission request expired", + }); + }); + + it("preserves the legacy sendRunCommand result contract", async () => { + const fetch = vi.fn().mockResolvedValue( + new Response(JSON.stringify({ error: "Run is unavailable" }), { + status: 503, + }), + ); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + await expect( + client.sendRunCommand("task-1", "run-1", "set_config_option"), + ).resolves.toEqual({ + success: false, + error: "Cloud command 'set_config_option' failed: 503 Run is unavailable", + }); + }); + + it("cancels a cloud task run with an optional reason", async () => { + const fetch = vi + .fn() + .mockResolvedValue( + new Response(JSON.stringify({ status: "cancelled" }), { status: 200 }), + ); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + await expect( + client.cancelTaskRun("task-1", "run-1", "user requested"), + ).resolves.toEqual({ status: "cancelled" }); + + expect(fetch).toHaveBeenCalledWith( + new URL( + "https://app.posthog.test/api/projects/42/tasks/task-1/runs/run-1/cancel/", + ), + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ reason: "user requested" }), + }), + ); + }); + + it("cancels a cloud task run with an empty body by default", async () => { + const fetch = vi + .fn() + .mockResolvedValue(new Response(null, { status: 204 })); + const client = new PostHogAPIClient( + "https://app.posthog.test", + async () => "token", + async () => "token", + 42, + { fetch }, + ); + + await expect(client.cancelTaskRun("task-1", "run-1")).resolves.toEqual({}); + + const request = fetch.mock.calls[0][1] as RequestInit; + expect(request.body).toBe(JSON.stringify({})); + }); + + it("builds cloud task config from the authenticated gateway catalog", async () => { + const fetch = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ + data: [ + { + id: "claude-opus-4-8", + owned_by: "anthropic", + context_window: 200000, + supports_streaming: true, + supports_vision: true, + allowed: true, + }, + { + id: "claude-fable-5", + owned_by: "anthropic", + context_window: 200000, + supports_streaming: true, + supports_vision: true, + allowed: false, + }, + ], + }), + { status: 200, headers: { "Content-Type": "application/json" } }, + ), + ); + const client = new PostHogAPIClient( + "https://eu.posthog.com", + async () => "token", + async () => "token", + 123, + { fetch }, + ); + + const options = await client.getCloudTaskConfigOptions("claude"); + + expect(fetch).toHaveBeenCalledWith( + new URL("https://gateway.eu.posthog.com/posthog_code/v1/models"), + expect.objectContaining({ method: "GET" }), + ); + expect(options.find((option) => option.category === "model")).toMatchObject( + { + currentValue: "claude-opus-4-8", + options: [ + expect.objectContaining({ value: "claude-opus-4-8" }), + expect.objectContaining({ + value: "claude-fable-5", + _meta: expect.any(Object), + }), + ], + }, + ); + }); + + it("uses the configured fetch implementation for task log URLs", async () => { + const fetch = vi + .fn() + .mockResolvedValue( + new Response( + '{"type":"notification","timestamp":"2026-07-21T00:00:00Z"}\n', + { status: 200 }, + ), + ); + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + { fetch }, + ); + vi.spyOn(client, "getTask").mockResolvedValue({ + id: "task-1", + task_number: 1, + slug: "task-1", + title: "Task", + description: "Task", + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:00:00Z", + origin_product: "user_created", + latest_run: { + id: "run-1", + task: "task-1", + team: 123, + branch: null, + status: "in_progress", + log_url: "https://logs.posthog.test/run-1.jsonl", + error_message: null, + output: null, + state: {}, + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:00:00Z", + completed_at: null, + }, + }); + + await expect(client.getTaskLogs("task-1")).resolves.toHaveLength(1); + expect(fetch).toHaveBeenCalledWith("https://logs.posthog.test/run-1.jsonl"); + }); + + it.each([ + { + label: "desktop default", + options: undefined, + expectedConnectFrom: "posthog_code", + expectedUserAgent: "posthog/desktop.hog.dev; version: unknown", + }, + { + label: "mobile configuration", + options: { + appVersion: "1.2.3", + userAgent: "posthog/mobile; version: 1.2.3", + githubConnectFrom: "posthog_mobile", + }, + expectedConnectFrom: "posthog_mobile", + expectedUserAgent: "posthog/mobile; version: 1.2.3", + }, + ])( + "uses $label identity for GitHub connections", + async ({ options, expectedConnectFrom, expectedUserAgent }) => { + const fetch = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ install_url: "https://github.com/login/oauth" }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ), + ); + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + { ...options, fetch }, + ); + + await expect(client.startGithubUserIntegrationConnect()).resolves.toEqual( + { + install_url: "https://github.com/login/oauth", + }, + ); + + expect(fetch).toHaveBeenCalledWith( + new URL( + "http://localhost:8000/api/users/@me/integrations/github/start/", + ), + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ + team_id: 123, + connect_from: expectedConnectFrom, + }), + }), + ); + expect(fetch.mock.calls[0][1].headers.get("User-Agent")).toBe( + expectedUserAgent, + ); + }, + ); + it("sends supported reasoning effort for cloud Codex runs", async () => { const client = new PostHogAPIClient( "http://localhost:8000", @@ -257,7 +586,13 @@ describe("PostHogAPIClient", () => { reasoningLevel: "high", initialPermissionMode: "auto", }), - ).resolves.toEqual({ id: "run-123", environment: "cloud" }); + ).resolves.toMatchObject({ + id: "run-123", + task: "task-123", + team: 123, + environment: "cloud", + status: "not_started", + }); expect(fetch).toHaveBeenCalledWith( expect.objectContaining({ @@ -435,7 +770,15 @@ describe("PostHogAPIClient", () => { pendingUserMessage: "Read the attached file first", pendingUserArtifactIds: ["artifact-1"], }), - ).resolves.toEqual({ id: "task-123", latest_run: { id: "run-123" } }); + ).resolves.toMatchObject({ + id: "task-123", + latest_run: { + id: "run-123", + task: "task-123", + team: 123, + status: "not_started", + }, + }); expect(fetch).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index 906e77f290..b2ef9fa8a8 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -4,18 +4,30 @@ import type { CloudMcpServerImport, CloudMcpServerRelayDesignation, CloudRunSource, + CreateTaskAutomationOptions, ExecutionMode, PrAuthorshipMode, SourceProduct, SourceType, StoredLogEntry, + TaskAutomation, TaskRunArtifactMetadata, + UpdateTaskAutomationOptions, } from "@posthog/shared"; import { + buildCloudTaskConfigOptions, + type CloudTaskConfigOption, + createTaskAutomationSchema, DISMISSAL_REASON_OPTIONS, type DismissalReasonOptionValue, + getCloudTaskGatewayUrl, isSupportedReasoningEffort, + normalizeGatewayModelsResponse, resolveCloudInitialPermissionMode, + taskAutomationListSchema, + taskAutomationSchema, + taskAutomationValidationErrorSchema, + updateTaskAutomationSchema, } from "@posthog/shared"; import type { AgentAnalyticsData, @@ -101,9 +113,18 @@ import { type HogQLGrid, shapeAgentAnalytics, } from "./agent-analytics"; -import { buildApiFetcher, requestErrorStatus } from "./fetcher"; +import { + ApiRequestError, + buildApiFetcher, + type FetchImplementation, + requestErrorStatus, +} from "./fetcher"; import { createApiClient, type Schemas } from "./generated"; import type { SpendAnalysisResponse } from "./spend-analysis"; +import { + normalizeTaskResponse, + normalizeTaskRunResponse, +} from "./task-normalization"; export interface ApiClientLogger { warn(...args: unknown[]): void; } @@ -122,6 +143,13 @@ export function setPosthogApiClientAppVersion(version: string): void { clientAppVersion = version; } +export interface PostHogAPIClientOptions { + fetch?: FetchImplementation; + appVersion?: string; + userAgent?: string | null; + githubConnectFrom?: string; +} + export function getPosthogApiClientAppVersion(): string { return clientAppVersion; } @@ -163,6 +191,36 @@ export class CloudUsageLimitError extends Error { } } +export class TaskAutomationValidationError extends Error { + readonly status = 400; + readonly code: string; + readonly attr: string | null; + + constructor(details: { + detail: string; + code: string; + attr: string | null; + }) { + super(details.detail); + this.name = "TaskAutomationValidationError"; + this.code = details.code; + this.attr = details.attr; + } +} + +function rethrowTaskAutomationError(error: unknown): never { + if (error instanceof ApiRequestError && error.status === 400) { + const validationError = taskAutomationValidationErrorSchema.safeParse( + error.body, + ); + if (validationError.success) { + throw new TaskAutomationValidationError(validationError.data); + } + } + + throw error; +} + export const MCP_CATEGORIES = [ { id: "all", label: "All" }, { id: "business", label: "Business Operations" }, @@ -581,7 +639,7 @@ export interface FinalizedTaskArtifactUpload { uploaded_at?: string; } -interface CloudRunOptions { +export interface CloudRunOptions { adapter?: Adapter; model?: string; reasoningLevel?: string; @@ -602,6 +660,56 @@ interface CloudRunOptions { relayedMcpServers?: CloudMcpServerRelayDesignation[]; } +export type CloudRunCommandMethod = + | "user_message" + | "permission_response" + | "set_config_option" + | "cancel" + | "close"; + +export class CloudCommandError extends Error { + readonly status: number; + readonly backendError: string | null; + readonly method: CloudRunCommandMethod; + + constructor( + method: CloudRunCommandMethod, + status: number, + backendError: string | null, + message: string, + ) { + super(message); + this.name = "CloudCommandError"; + this.method = method; + this.status = status; + this.backendError = backendError; + } + + isSandboxInactive(): boolean { + const backendError = this.backendError?.toLowerCase(); + return ( + this.status === 404 || + backendError?.includes("no active sandbox") === true || + backendError?.includes("returned 404") === true + ); + } +} + +function cloudCommandBackendError(payload: unknown): string | null { + if (typeof payload === "string") return payload || null; + if (!payload || typeof payload !== "object") return null; + + const error = "error" in payload ? payload.error : null; + if (typeof error === "string") return error || null; + if (error && typeof error === "object" && "message" in error) { + return typeof error.message === "string" ? error.message : null; + } + if ("message" in payload && typeof payload.message === "string") { + return payload.message; + } + return null; +} + interface CreateTaskRunOptions extends CloudRunOptions { environment?: "local" | "cloud"; mode?: "interactive" | "background"; @@ -1342,19 +1450,28 @@ function previewTokenHeader( export class PostHogAPIClient { private api: ReturnType; private _teamId: number | null = null; + private githubConnectFrom: string; + private readonly fetch: FetchImplementation; + private readonly apiHost: string; constructor( apiHost: string, getAccessToken: () => Promise, refreshAccessToken: () => Promise, teamId?: number, + options: PostHogAPIClientOptions = {}, ) { const baseUrl = apiHost.endsWith("/") ? apiHost.slice(0, -1) : apiHost; + this.apiHost = baseUrl; + this.githubConnectFrom = options.githubConnectFrom ?? "posthog_code"; + this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis); this.api = createApiClient( buildApiFetcher({ getAccessToken, refreshAccessToken, - appVersion: clientAppVersion, + appVersion: options.appVersion ?? clientAppVersion, + fetch: options.fetch, + userAgent: options.userAgent, }), baseUrl, ); @@ -1391,6 +1508,21 @@ export class PostHogAPIClient { return data; } + async getCloudTaskConfigOptions( + adapter: Adapter = "claude", + ): Promise { + const url = new URL(`${getCloudTaskGatewayUrl(this.apiHost)}/v1/models`); + const response = await this.api.fetcher.fetch({ + method: "get", + url, + path: url.pathname, + }); + return buildCloudTaskConfigOptions( + normalizeGatewayModelsResponse(await response.json()), + adapter, + ); + } + // Desktop file system — the backend surface that backs canvas channels // (top-level folders) and dashboards. These routes aren't in the generated // OpenAPI client, so we use the raw fetcher. @@ -1755,7 +1887,10 @@ export class PostHogAPIClient { url, path: urlPath, overrides: { - body: JSON.stringify({ team_id: id, connect_from: "posthog_code" }), + body: JSON.stringify({ + team_id: id, + connect_from: this.githubConnectFrom, + }), }, }); if (!response.ok) { @@ -2257,7 +2392,7 @@ export class PostHogAPIClient { originProduct?: string; internal?: boolean; channel?: string; - }) { + }): Promise { const teamId = await this.getTeamId(); const params: Record = { limit: 500, @@ -2288,7 +2423,9 @@ export class PostHogAPIClient { query: params, }); - return data.results ?? []; + return (data.results ?? []).map((task) => + normalizeTaskResponse(task, { teamId }), + ); } async getTaskSummaries(ids: string[]) { @@ -2330,7 +2467,102 @@ export class PostHogAPIClient { const data = await this.api.get(`/api/projects/{project_id}/tasks/{id}/`, { path: { project_id: teamId.toString(), id: taskId }, }); - return data as unknown as Task; + return normalizeTaskResponse(data, { teamId }); + } + + async listTaskAutomations(options?: { + limit?: number; + offset?: number; + }): Promise { + const teamId = await this.getTeamId(); + const data = await this.api.get( + `/api/projects/{project_id}/task_automations/`, + { + path: { project_id: teamId.toString() }, + query: { + limit: options?.limit ?? 500, + ...(options?.offset === undefined ? {} : { offset: options.offset }), + }, + }, + ); + + return taskAutomationListSchema.parse(data).results; + } + + async getTaskAutomation(automationId: string): Promise { + const teamId = await this.getTeamId(); + const data = await this.api.get( + `/api/projects/{project_id}/task_automations/{id}/`, + { + path: { project_id: teamId.toString(), id: automationId }, + }, + ); + + return taskAutomationSchema.parse(data); + } + + async createTaskAutomation( + options: CreateTaskAutomationOptions, + ): Promise { + const teamId = await this.getTeamId(); + const body = createTaskAutomationSchema.parse(options); + + try { + const data = await this.api.post( + `/api/projects/{project_id}/task_automations/`, + { + path: { project_id: teamId.toString() }, + body: body as Schemas.TaskAutomation, + }, + ); + return taskAutomationSchema.parse(data); + } catch (error) { + rethrowTaskAutomationError(error); + } + } + + async updateTaskAutomation( + automationId: string, + updates: UpdateTaskAutomationOptions, + ): Promise { + const teamId = await this.getTeamId(); + const body = updateTaskAutomationSchema.parse(updates); + + try { + const data = await this.api.patch( + `/api/projects/{project_id}/task_automations/{id}/`, + { + path: { project_id: teamId.toString(), id: automationId }, + body, + }, + ); + return taskAutomationSchema.parse(data); + } catch (error) { + rethrowTaskAutomationError(error); + } + } + + async deleteTaskAutomation(automationId: string): Promise { + const teamId = await this.getTeamId(); + await this.api.delete(`/api/projects/{project_id}/task_automations/{id}/`, { + path: { project_id: teamId.toString(), id: automationId }, + }); + } + + async runTaskAutomation(automationId: string): Promise { + const teamId = await this.getTeamId(); + const path = `/api/projects/${teamId}/task_automations/${automationId}/run/`; + + try { + const response = await this.api.fetcher.fetch({ + method: "post", + path, + url: new URL(`${this.api.baseUrl}${path}`), + }); + return taskAutomationSchema.parse(await response.json()); + } catch (error) { + rethrowTaskAutomationError(error); + } } async createTask( @@ -2357,7 +2589,7 @@ export class PostHogAPIClient { pending_user_artifact_ids?: string[]; auto_publish?: boolean; }, - ) { + ): Promise { const teamId = await this.getTeamId(); const { origin_product: originProduct, ...taskOptions } = options; @@ -2369,10 +2601,13 @@ export class PostHogAPIClient { } as unknown as Schemas.Task, }); - return data; + return normalizeTaskResponse(data, { teamId }); } - async updateTask(taskId: string, updates: Partial) { + async updateTask( + taskId: string, + updates: Partial, + ): Promise { const teamId = await this.getTeamId(); const data = await this.api.patch( `/api/projects/{project_id}/tasks/{id}/`, @@ -2382,7 +2617,7 @@ export class PostHogAPIClient { }, ); - return data; + return normalizeTaskResponse(data, { teamId }); } async deleteTask(taskId: string) { @@ -2681,9 +2916,28 @@ export class PostHogAPIClient { async sendRunCommand( taskId: string, runId: string, - method: "user_message" | "cancel" | "close", + method: CloudRunCommandMethod, params?: Record, ): Promise<{ success: boolean; result?: unknown; error?: string }> { + try { + return { + success: true, + result: await this.sendCloudRunCommand(taskId, runId, method, params), + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + } + + async sendCloudRunCommand( + taskId: string, + runId: string, + method: CloudRunCommandMethod, + params: Record = {}, + ): Promise { const teamId = await this.getTeamId(); const url = new URL( `${this.api.baseUrl}/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/command/`, @@ -2691,7 +2945,7 @@ export class PostHogAPIClient { const body = { jsonrpc: "2.0", method, - params: params ?? {}, + params, id: `posthog-code-${Date.now()}`, }; @@ -2705,39 +2959,54 @@ export class PostHogAPIClient { }, }); - if (!response.ok) { - const errorText = await response.text().catch(() => ""); - let errorMessage = `Command failed: ${response.statusText}`; - try { - const errorJson = JSON.parse(errorText); - errorMessage = - errorJson.error?.message ?? errorJson.error ?? errorMessage; - } catch { - if (errorText) errorMessage = errorText; - } - return { success: false, error: errorMessage }; - } - const data = (await response.json()) as { - error?: { message?: string }; + error?: unknown; result?: unknown; }; if (data.error) { - return { - success: false, - error: data.error.message ?? JSON.stringify(data.error), - }; + const backendError = cloudCommandBackendError(data); + throw new CloudCommandError( + method, + response.status, + backendError, + `Cloud command '${method}' error: ${backendError ?? JSON.stringify(data.error)}`, + ); } - return { success: true, result: data.result }; + return data.result; } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : "Unknown error", - }; + if (error instanceof CloudCommandError) throw error; + if (error instanceof ApiRequestError) { + const backendError = cloudCommandBackendError(error.body); + throw new CloudCommandError( + method, + error.status, + backendError, + `Cloud command '${method}' failed: ${error.status}${backendError ? ` ${backendError}` : ""}`, + ); + } + throw error; } } + async cancelTaskRun( + taskId: string, + runId: string, + reason?: string, + ): Promise<{ status?: string }> { + const teamId = await this.getTeamId(); + const path = `/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/cancel/`; + const response = await this.api.fetcher.fetch({ + method: "post", + url: new URL(`${this.api.baseUrl}${path}`), + path, + overrides: { + body: JSON.stringify(reason ? { reason } : {}), + }, + }); + return (await response.json().catch(() => ({}))) as { status?: string }; + } + async runTaskInCloud( taskId: string, branch?: string | null, @@ -2761,7 +3030,7 @@ export class PostHogAPIClient { }), ); - return data as unknown as Task; + return normalizeTaskResponse(data, { teamId }); } async warmTask(options: { @@ -3001,7 +3270,8 @@ export class PostHogAPIClient { throw new Error(`Failed to resume run in cloud: ${response.statusText}`); } - return (await response.json()) as TaskRun; + const data = (await response.json()) as Schemas.TaskRunDetail; + return normalizeTaskRunResponse(data, { teamId, taskId }); } async listTaskRuns(taskId: string): Promise { @@ -3019,8 +3289,11 @@ export class PostHogAPIClient { throw new Error(`Failed to fetch task runs: ${response.statusText}`); } - const data = (await response.json()) as { results?: TaskRun[] }; - return data.results ?? []; + const data = + (await response.json()) as Partial; + return (data.results ?? []).map((run) => + normalizeTaskRunResponse(run, { teamId, taskId }), + ); } async getTaskRun(taskId: string, runId: string): Promise { @@ -3038,7 +3311,8 @@ export class PostHogAPIClient { throw new Error(`Failed to fetch task run: ${response.statusText}`); } - return (await response.json()) as TaskRun; + const data = (await response.json()) as Schemas.TaskRunDetail; + return normalizeTaskRunResponse(data, { teamId, taskId }); } async createTaskRun( @@ -3070,7 +3344,8 @@ export class PostHogAPIClient { throw new Error(`Failed to create task run: ${response.statusText}`); } - return (await response.json()) as TaskRun; + const data = (await response.json()) as Schemas.TaskRunDetail; + return normalizeTaskRunResponse(data, { teamId, taskId }); } async startTaskRun( @@ -3100,7 +3375,8 @@ export class PostHogAPIClient { throw new Error(`Failed to start task run: ${response.statusText}`); } - return (await response.json()) as Task; + const data = (await response.json()) as Schemas.Task; + return normalizeTaskResponse(data, { teamId }); } async updateTaskRun( @@ -3125,7 +3401,7 @@ export class PostHogAPIClient { body: updates as Record, }, ); - return data as unknown as TaskRun; + return normalizeTaskRunResponse(data, { teamId, taskId }); } /** @@ -3215,14 +3491,14 @@ export class PostHogAPIClient { async getTaskLogs(taskId: string): Promise { try { - const task = (await this.getTask(taskId)) as unknown as Task; + const task = await this.getTask(taskId); const logUrl = task?.latest_run?.log_url; if (!logUrl) { return []; } - const response = await fetch(logUrl); + const response = await this.fetch(logUrl); if (!response.ok) { log.warn( diff --git a/packages/api-client/src/task-normalization.test.ts b/packages/api-client/src/task-normalization.test.ts new file mode 100644 index 0000000000..9c9739a51f --- /dev/null +++ b/packages/api-client/src/task-normalization.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it } from "vitest"; +import { + normalizeTaskResponse, + normalizeTaskRunResponse, +} from "./task-normalization"; + +describe("task response normalization", () => { + it("normalizes legacy started runs and nullable generated fields", () => { + expect( + normalizeTaskRunResponse( + { + id: "run-1", + task: "task-1", + status: "started", + branch: null, + stage: null, + runtime_adapter: null, + model: null, + reasoning_effort: null, + log_url: null, + error_message: null, + output: null, + state: null, + artifacts: [ + { + id: "artifact-1", + name: "result.txt", + type: "legacy_type", + storage_path: "tasks/result.txt", + uploaded_at: "2026-07-21T00:00:00Z", + }, + ], + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:01:00Z", + completed_at: null, + }, + { teamId: 123 }, + ), + ).toEqual({ + id: "run-1", + task: "task-1", + team: 123, + branch: null, + stage: null, + runtime_adapter: null, + model: null, + reasoning_effort: null, + status: "in_progress", + log_url: "", + error_message: null, + output: null, + state: {}, + artifacts: [ + { + id: "artifact-1", + name: "result.txt", + type: "artifact", + storage_path: "tasks/result.txt", + uploaded_at: "2026-07-21T00:00:00Z", + }, + ], + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:01:00Z", + completed_at: null, + }); + }); + + it("normalizes task responses and their generated latest-run records", () => { + expect( + normalizeTaskResponse( + { + id: "task-1", + task_number: null, + slug: "task-1", + repository: null, + github_integration: null, + github_user_integration: null, + json_schema: null, + signal_report: null, + channel: null, + latest_run: { + id: "run-1", + status: "started", + log_url: null, + }, + created_at: "2026-07-21T00:00:00Z", + updated_at: "2026-07-21T00:01:00Z", + }, + { teamId: 123 }, + ), + ).toMatchObject({ + id: "task-1", + task_number: null, + slug: "task-1", + title: "", + description: "", + origin_product: "", + repository: null, + github_integration: null, + github_user_integration: null, + json_schema: null, + signal_report: null, + channel: null, + latest_run: { + id: "run-1", + task: "task-1", + team: 123, + status: "in_progress", + log_url: "", + output: null, + state: {}, + }, + }); + }); +}); diff --git a/packages/api-client/src/task-normalization.ts b/packages/api-client/src/task-normalization.ts new file mode 100644 index 0000000000..9571a5b27e --- /dev/null +++ b/packages/api-client/src/task-normalization.ts @@ -0,0 +1,203 @@ +import type { + ArtifactType, + Task, + TaskRun, + TaskRunArtifact, + TaskRunArtifactMetadata, + TaskRunStatus, +} from "@posthog/shared/domain-types"; +import type { Schemas } from "./generated"; + +type TaskRunResponseDTO = Partial< + Omit +> & { + id: string; + artifacts?: Array< + Schemas.TaskRunArtifactResponse & { metadata?: unknown } + > | null; + status?: Schemas.StatusA35Enum | "started" | null; + team?: number | null; +}; + +type TaskResponseDTO = Partial< + Omit +> & { + id: string; + channel?: string | null; + created_by?: Schemas.UserBasic | null; + github_user_integration?: string | null; + json_schema?: unknown | null; + latest_run?: Record | null; + runtime?: unknown; +}; + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function isTaskRunResponseDTO(value: unknown): value is TaskRunResponseDTO { + return isRecord(value) && typeof value.id === "string"; +} + +function normalizeTaskRunStatus(status: unknown): TaskRunStatus { + switch (status) { + case "started": + return "in_progress"; + case "not_started": + case "queued": + case "in_progress": + case "completed": + case "failed": + case "cancelled": + return status; + default: + return "not_started"; + } +} + +function normalizeArtifactType(type: string): ArtifactType { + switch (type) { + case "plan": + case "context": + case "reference": + case "output": + case "artifact": + case "user_attachment": + case "skill_bundle": + return type; + default: + return "artifact"; + } +} + +function normalizeArtifactMetadata( + value: unknown, +): TaskRunArtifactMetadata | undefined { + if ( + !isRecord(value) || + typeof value.skill_name !== "string" || + (value.skill_source !== "user" && + value.skill_source !== "repo" && + value.skill_source !== "marketplace" && + value.skill_source !== "codex") + ) { + return undefined; + } + if ( + typeof value.content_sha256 !== "string" || + value.bundle_format !== "zip" || + typeof value.schema_version !== "number" + ) { + return undefined; + } + + return { + skill_name: value.skill_name, + skill_source: value.skill_source, + content_sha256: value.content_sha256, + bundle_format: value.bundle_format, + schema_version: value.schema_version, + }; +} + +function normalizeTaskRunArtifact( + artifact: NonNullable[number], +): TaskRunArtifact { + const metadata = normalizeArtifactMetadata(artifact.metadata); + + return { + ...(artifact.id === undefined ? {} : { id: artifact.id }), + name: artifact.name, + type: normalizeArtifactType(artifact.type), + ...(artifact.source === undefined ? {} : { source: artifact.source }), + ...(artifact.size === undefined ? {} : { size: artifact.size }), + ...(artifact.content_type === undefined + ? {} + : { content_type: artifact.content_type }), + ...(metadata === undefined ? {} : { metadata }), + ...(artifact.storage_path === undefined + ? {} + : { storage_path: artifact.storage_path }), + ...(artifact.uploaded_at === undefined + ? {} + : { uploaded_at: artifact.uploaded_at }), + }; +} + +export function normalizeTaskRunResponse( + dto: TaskRunResponseDTO, + context: { teamId: number; taskId?: string }, +): TaskRun { + return { + id: dto.id, + task: dto.task ?? context.taskId ?? "", + team: dto.team ?? context.teamId, + branch: dto.branch ?? null, + ...(dto.runtime_adapter === undefined + ? {} + : { runtime_adapter: dto.runtime_adapter }), + ...(dto.model === undefined ? {} : { model: dto.model }), + ...(dto.reasoning_effort === undefined + ? {} + : { reasoning_effort: dto.reasoning_effort }), + ...(dto.stage === undefined ? {} : { stage: dto.stage }), + ...(dto.environment === undefined ? {} : { environment: dto.environment }), + status: normalizeTaskRunStatus(dto.status), + log_url: dto.log_url ?? "", + error_message: dto.error_message ?? null, + output: isRecord(dto.output) ? dto.output : null, + state: isRecord(dto.state) ? dto.state : {}, + ...(dto.artifacts == null + ? {} + : { artifacts: dto.artifacts.map(normalizeTaskRunArtifact) }), + created_at: dto.created_at ?? "", + updated_at: dto.updated_at ?? "", + completed_at: dto.completed_at ?? null, + }; +} + +export function normalizeTaskResponse( + dto: TaskResponseDTO, + context: { teamId: number }, +): Task { + const jsonSchema = isRecord(dto.json_schema) ? dto.json_schema : null; + const runtime = + dto.runtime === "acp" || dto.runtime === "pi" ? dto.runtime : undefined; + + const latestRun = isTaskRunResponseDTO(dto.latest_run) + ? normalizeTaskRunResponse(dto.latest_run, { + teamId: context.teamId, + taskId: dto.id, + }) + : undefined; + + return { + id: dto.id, + task_number: dto.task_number ?? null, + slug: dto.slug ?? "", + title: dto.title ?? "", + ...(dto.title_manually_set === undefined + ? {} + : { title_manually_set: dto.title_manually_set }), + description: dto.description ?? "", + created_at: dto.created_at ?? "", + updated_at: dto.updated_at ?? "", + ...(dto.created_by === undefined ? {} : { created_by: dto.created_by }), + origin_product: dto.origin_product ?? "", + ...(dto.repository === undefined ? {} : { repository: dto.repository }), + ...(dto.github_integration === undefined + ? {} + : { github_integration: dto.github_integration }), + ...(dto.github_user_integration === undefined + ? {} + : { github_user_integration: dto.github_user_integration }), + ...(dto.json_schema === undefined ? {} : { json_schema: jsonSchema }), + ...(dto.signal_report === undefined + ? {} + : { signal_report: dto.signal_report }), + ...(dto.internal === undefined ? {} : { internal: dto.internal }), + ...(runtime === undefined ? {} : { runtime }), + ...(dto.channel === undefined ? {} : { channel: dto.channel }), + ...(latestRun === undefined ? {} : { latest_run: latestRun }), + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 65ef2c8cbf..5cdfefdf93 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -869,9 +869,6 @@ importers: packages/api-client: dependencies: - '@posthog/agent': - specifier: workspace:* - version: link:../agent '@posthog/shared': specifier: workspace:* version: link:../shared @@ -19215,13 +19212,6 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.3 - optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -22659,7 +22649,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': From 01aec6d6b84e9751dec936413b67bc88700bb125 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:18:39 +0300 Subject: [PATCH 08/42] refactor(core): extract cloud task policies Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../automations/automationSchedule.test.ts | 223 ++++++++++++++++++ .../src/automations/automationSchedule.ts | 216 +++++++++++++++++ .../src/automations/automationStatus.test.ts | 80 +++++++ .../core/src/automations/automationStatus.ts | 83 +++++++ packages/core/src/inbox/artefacts.test.ts | 159 ++++++++++++- packages/core/src/inbox/artefacts.ts | 86 +++++++ .../core/src/inbox/reportFiltering.test.ts | 10 + packages/core/src/inbox/reportFiltering.ts | 12 +- .../core/src/sessions/cloudSessionConfig.ts | 9 +- packages/core/src/sessions/executionModes.ts | 4 +- .../sessions/portableSessionEvents.test.ts | 71 ++++++ .../src/sessions/portableSessionEvents.ts | 98 ++++++++ .../core/src/sessions/sessionActivity.test.ts | 152 ++++++++++++ packages/core/src/sessions/sessionActivity.ts | 129 ++++++++++ packages/core/src/tasks/taskActivity.test.ts | 132 +++++++++++ packages/core/src/tasks/taskActivity.ts | 45 ++++ packages/core/src/tasks/taskArchive.test.ts | 44 ++++ packages/core/src/tasks/taskArchive.ts | 6 + .../src/tasks/taskStatusPresentation.test.ts | 69 ++++++ .../core/src/tasks/taskStatusPresentation.ts | 37 +++ 20 files changed, 1659 insertions(+), 6 deletions(-) create mode 100644 packages/core/src/automations/automationSchedule.test.ts create mode 100644 packages/core/src/automations/automationSchedule.ts create mode 100644 packages/core/src/automations/automationStatus.test.ts create mode 100644 packages/core/src/automations/automationStatus.ts create mode 100644 packages/core/src/sessions/portableSessionEvents.test.ts create mode 100644 packages/core/src/sessions/portableSessionEvents.ts create mode 100644 packages/core/src/sessions/sessionActivity.test.ts create mode 100644 packages/core/src/sessions/sessionActivity.ts create mode 100644 packages/core/src/tasks/taskActivity.test.ts create mode 100644 packages/core/src/tasks/taskActivity.ts create mode 100644 packages/core/src/tasks/taskArchive.test.ts create mode 100644 packages/core/src/tasks/taskArchive.ts create mode 100644 packages/core/src/tasks/taskStatusPresentation.test.ts create mode 100644 packages/core/src/tasks/taskStatusPresentation.ts diff --git a/packages/core/src/automations/automationSchedule.test.ts b/packages/core/src/automations/automationSchedule.test.ts new file mode 100644 index 0000000000..5deb2487a0 --- /dev/null +++ b/packages/core/src/automations/automationSchedule.test.ts @@ -0,0 +1,223 @@ +import { describe, expect, it } from "vitest"; +import { + type AutomationScheduleDraft, + buildCronExpression, + createDefaultScheduleDraft, + deriveAutomationName, + formatAutomationScheduleSummary, + formatScheduleSummary, + parseCronExpression, + sanitizeHour, + sanitizeMinute, + WEEKDAY_OPTIONS, +} from "./automationSchedule"; + +describe("automationSchedule", () => { + it("creates the default daily schedule draft", () => { + expect(createDefaultScheduleDraft()).toEqual({ + mode: "daily", + hour: "09", + minute: "00", + weekday: "1", + rawCron: "0 9 * * *", + }); + }); + + it("provides cron weekday values in display order", () => { + expect(WEEKDAY_OPTIONS).toEqual([ + { value: "1", label: "Mon" }, + { value: "2", label: "Tue" }, + { value: "3", label: "Wed" }, + { value: "4", label: "Thu" }, + { value: "5", label: "Fri" }, + { value: "6", label: "Sat" }, + { value: "0", label: "Sun" }, + ]); + }); + + it.each([ + ["", ""], + ["a", ""], + ["7", "07"], + ["09", "09"], + ["2x3", "23"], + ["24", "23"], + ["999", "23"], + ])("sanitizes hour input %j to %j", (input, expected) => { + expect(sanitizeHour(input)).toBe(expected); + }); + + it.each([ + ["", ""], + ["a", ""], + ["7", "07"], + ["09", "09"], + ["5x9", "59"], + ["60", "59"], + ["999", "59"], + ])("sanitizes minute input %j to %j", (input, expected) => { + expect(sanitizeMinute(input)).toBe(expected); + }); + + it.each<{ + name: string; + changes: Partial; + expected: string; + }>([ + { + name: "hourly", + changes: { mode: "hourly", minute: "15" }, + expected: "15 * * * *", + }, + { + name: "daily", + changes: { mode: "daily", hour: "09", minute: "15" }, + expected: "15 9 * * *", + }, + { + name: "weekdays", + changes: { mode: "weekdays", hour: "10", minute: "00" }, + expected: "0 10 * * 1-5", + }, + { + name: "weekly", + changes: { + mode: "weekly", + hour: "11", + minute: "30", + weekday: "4", + }, + expected: "30 11 * * 4", + }, + { + name: "weekly with a missing weekday", + changes: { mode: "weekly", weekday: "" }, + expected: "0 9 * * 1", + }, + { + name: "preset with missing time values", + changes: { mode: "daily", hour: "", minute: "" }, + expected: "0 9 * * *", + }, + { + name: "custom", + changes: { mode: "custom", rawCron: " */15 * * * * " }, + expected: "*/15 * * * *", + }, + ])("builds the $name cron expression", ({ changes, expected }) => { + expect( + buildCronExpression({ ...createDefaultScheduleDraft(), ...changes }), + ).toBe(expected); + }); + + it.each([ + [ + "15 * * * *", + { + mode: "hourly", + hour: "09", + minute: "15", + weekday: "*", + rawCron: "15 * * * *", + }, + ], + [ + "0 9 * * *", + { + mode: "daily", + hour: "09", + minute: "00", + weekday: "*", + rawCron: "0 9 * * *", + }, + ], + [ + "0 9 * * 1-5", + { + mode: "weekdays", + hour: "09", + minute: "00", + weekday: "1", + rawCron: "0 9 * * 1-5", + }, + ], + [ + "30 14 * * 2", + { + mode: "weekly", + hour: "14", + minute: "30", + weekday: "2", + rawCron: "30 14 * * 2", + }, + ], + ] as const)("parses %s into a schedule draft", (cron, expected) => { + expect(parseCronExpression(cron)).toEqual(expected); + }); + + it.each(["*/15 * * * *", "0 9 1 * *", "0 9 * 1 *", "0 9 * * 1,3"])( + "keeps unsupported cron expression %s in custom mode", + (cron) => { + expect(parseCronExpression(cron)).toMatchObject({ + mode: "custom", + rawCron: cron, + }); + }, + ); + + it("normalizes surrounding and repeated cron whitespace", () => { + expect(parseCronExpression(" 5 8 * * * ")).toEqual({ + mode: "daily", + hour: "08", + minute: "05", + weekday: "*", + rawCron: "5 8 * * *", + }); + }); + + it("uses default draft fields for a cron expression with the wrong arity", () => { + expect(parseCronExpression("0 9 * *")).toEqual({ + mode: "custom", + hour: "09", + minute: "00", + weekday: "1", + rawCron: "0 9 * *", + }); + }); + + it("derives a compact name from the first non-empty prompt line", () => { + expect( + deriveAutomationName( + "\n Review every open PostHog PR for stale comments \nIgnore this line", + ), + ).toBe("Review every open PostHog PR for stale comments"); + }); + + it("returns an empty name for a blank prompt", () => { + expect(deriveAutomationName(" \n\t\n ")).toBe(""); + }); + + it("limits derived names to 80 characters", () => { + expect(deriveAutomationName("a".repeat(100))).toBe("a".repeat(80)); + }); + + it.each([ + ["15 * * * *", "Europe/London", "Every hour at :15 · Europe/London"], + ["0 9 * * *", null, "Daily at 09:00"], + ["0 9 * * 1-5", "UTC", "Weekdays at 09:00 · UTC"], + ["30 14 * * 2", undefined, "Tue at 14:30"], + ["30 14 * * 7", "UTC", "Weekly at 14:30 · UTC"], + ["*/15 * * * *", "UTC", "Custom schedule · UTC"], + ])("formats %s with timezone %j", (cronExpression, timezone, expected) => { + expect(formatScheduleSummary(cronExpression, timezone)).toBe(expected); + }); + + it("formats a schedule from an automation-shaped input", () => { + expect( + formatAutomationScheduleSummary({ + cron_expression: "0 18 * * 0", + timezone: "America/New_York", + }), + ).toBe("Sun at 18:00 · America/New_York"); + }); +}); diff --git a/packages/core/src/automations/automationSchedule.ts b/packages/core/src/automations/automationSchedule.ts new file mode 100644 index 0000000000..527e8da497 --- /dev/null +++ b/packages/core/src/automations/automationSchedule.ts @@ -0,0 +1,216 @@ +export type AutomationScheduleMode = + | "hourly" + | "daily" + | "weekdays" + | "weekly" + | "custom"; + +export interface AutomationScheduleDraft { + mode: AutomationScheduleMode; + hour: string; + minute: string; + weekday: string; + rawCron: string; +} + +export interface AutomationScheduleSummaryInput { + cron_expression: string; + timezone?: string | null; +} + +export const WEEKDAY_OPTIONS = [ + { value: "1", label: "Mon" }, + { value: "2", label: "Tue" }, + { value: "3", label: "Wed" }, + { value: "4", label: "Thu" }, + { value: "5", label: "Fri" }, + { value: "6", label: "Sat" }, + { value: "0", label: "Sun" }, +] as const; + +export function createDefaultScheduleDraft(): AutomationScheduleDraft { + return { + mode: "daily", + hour: "09", + minute: "00", + weekday: "1", + rawCron: "0 9 * * *", + }; +} + +function padTimePart(value: string): string { + return value.padStart(2, "0"); +} + +export function sanitizeHour(value: string): string { + const digitsOnly = value.replace(/\D/g, "").slice(0, 2); + if (!digitsOnly) { + return ""; + } + + return String(Math.min(23, Number(digitsOnly))).padStart(2, "0"); +} + +export function sanitizeMinute(value: string): string { + const digitsOnly = value.replace(/\D/g, "").slice(0, 2); + if (!digitsOnly) { + return ""; + } + + return String(Math.min(59, Number(digitsOnly))).padStart(2, "0"); +} + +export function buildCronExpression(draft: AutomationScheduleDraft): string { + if (draft.mode === "custom") { + return draft.rawCron.trim(); + } + + const minute = draft.minute ? String(Number(draft.minute)) : "0"; + const hour = draft.hour ? String(Number(draft.hour)) : "9"; + + switch (draft.mode) { + case "hourly": + return `${minute} * * * *`; + case "weekdays": + return `${minute} ${hour} * * 1-5`; + case "weekly": + return `${minute} ${hour} * * ${draft.weekday || "1"}`; + default: + return `${minute} ${hour} * * *`; + } +} + +export function parseCronExpression( + cronExpression: string, +): AutomationScheduleDraft { + const normalized = cronExpression.trim(); + const parts = normalized.split(/\s+/); + + if (parts.length !== 5) { + return { + ...createDefaultScheduleDraft(), + mode: "custom", + rawCron: normalized, + }; + } + + const [minute, hour, dayOfMonth, month, dayOfWeek] = parts; + const isNumericMinute = /^\d{1,2}$/.test(minute); + const isNumericHour = /^\d{1,2}$/.test(hour); + const draftBase = { + hour: padTimePart(hour), + minute: padTimePart(minute), + weekday: dayOfWeek, + rawCron: normalized, + }; + + if ( + isNumericMinute && + hour === "*" && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + return { + ...draftBase, + mode: "hourly", + hour: "09", + }; + } + + if ( + isNumericMinute && + isNumericHour && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + return { + ...draftBase, + mode: "daily", + }; + } + + if ( + isNumericMinute && + isNumericHour && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "1-5" + ) { + return { + ...draftBase, + mode: "weekdays", + weekday: "1", + }; + } + + if ( + isNumericMinute && + isNumericHour && + dayOfMonth === "*" && + month === "*" && + /^\d$/.test(dayOfWeek) + ) { + return { + ...draftBase, + mode: "weekly", + }; + } + + return { + ...draftBase, + mode: "custom", + }; +} + +export function deriveAutomationName(prompt: string): string { + const normalized = prompt + .split("\n") + .map((line) => line.trim()) + .find(Boolean); + + if (!normalized) { + return ""; + } + + return normalized.replace(/\s+/g, " ").slice(0, 80); +} + +function formatTime(hour: string, minute: string): string { + return `${padTimePart(hour)}:${padTimePart(minute)}`; +} + +export function formatScheduleSummary( + cronExpression: string, + timezone: string | null | undefined, +): string { + const draft = parseCronExpression(cronExpression); + const suffix = timezone ? ` · ${timezone}` : ""; + + switch (draft.mode) { + case "hourly": + return `Every hour at :${padTimePart(draft.minute)}${suffix}`; + case "weekdays": + return `Weekdays at ${formatTime(draft.hour, draft.minute)}${suffix}`; + case "weekly": { + const label = + WEEKDAY_OPTIONS.find((option) => option.value === draft.weekday) + ?.label ?? "Weekly"; + return `${label} at ${formatTime(draft.hour, draft.minute)}${suffix}`; + } + case "custom": + return `Custom schedule${suffix}`; + default: + return `Daily at ${formatTime(draft.hour, draft.minute)}${suffix}`; + } +} + +export function formatAutomationScheduleSummary( + automation: AutomationScheduleSummaryInput, +): string { + return formatScheduleSummary( + automation.cron_expression, + automation.timezone ?? null, + ); +} diff --git a/packages/core/src/automations/automationStatus.test.ts b/packages/core/src/automations/automationStatus.test.ts new file mode 100644 index 0000000000..862fc3c0b5 --- /dev/null +++ b/packages/core/src/automations/automationStatus.test.ts @@ -0,0 +1,80 @@ +import { describe, expect, it } from "vitest"; +import { + type AutomationStatusPresentation, + type AutomationTaskRunStatus, + getAutomationStatusPresentation, +} from "./automationStatus"; + +describe("automationStatus", () => { + it.each<{ + status: AutomationTaskRunStatus; + expected: AutomationStatusPresentation | null; + }>([ + { + status: "not_started", + expected: { label: "Queued", tone: "warning", iconKind: "queued" }, + }, + { + status: "queued", + expected: { label: "Queued", tone: "warning", iconKind: "queued" }, + }, + { status: "started", expected: null }, + { status: "in_progress", expected: null }, + { + status: "completed", + expected: { label: "Success", tone: "success", iconKind: "success" }, + }, + { + status: "failed", + expected: { label: "Failed", tone: "error", iconKind: "failed" }, + }, + { + status: "cancelled", + expected: { label: "Failed", tone: "error", iconKind: "failed" }, + }, + ])( + "maps task-run status $status to renderer-neutral presentation data", + ({ status, expected }) => { + expect( + getAutomationStatusPresentation({ + lastRunStatus: "success", + lastTaskRunStatus: status, + }), + ).toEqual(expected); + }, + ); + + it.each([ + ["running", null], + ["success", { label: "Success", tone: "success", iconKind: "success" }], + ["failed", { label: "Failed", tone: "error", iconKind: "failed" }], + [null, { label: "Never run", tone: "neutral", iconKind: "never-run" }], + ["unknown", { label: "Never run", tone: "neutral", iconKind: "never-run" }], + ] as const)( + "falls back from automation status %j to semantic presentation data", + (lastRunStatus, expected) => { + expect(getAutomationStatusPresentation({ lastRunStatus })).toEqual( + expected, + ); + }, + ); + + it("prioritizes linked task-run detail over the automation-level status", () => { + expect( + getAutomationStatusPresentation({ + lastRunStatus: "failed", + lastTaskRunStatus: "completed", + }), + ).toEqual({ + label: "Success", + tone: "success", + iconKind: "success", + }); + }); + + it("does not expose renderer-specific class names", () => { + expect( + getAutomationStatusPresentation({ lastRunStatus: "success" }), + ).not.toHaveProperty("className"); + }); +}); diff --git a/packages/core/src/automations/automationStatus.ts b/packages/core/src/automations/automationStatus.ts new file mode 100644 index 0000000000..4bcc13735e --- /dev/null +++ b/packages/core/src/automations/automationStatus.ts @@ -0,0 +1,83 @@ +export type AutomationTaskRunStatus = + | "not_started" + | "queued" + | "started" + | "in_progress" + | "completed" + | "failed" + | "cancelled"; + +export interface AutomationStatusInput { + lastRunStatus: string | null; + lastTaskRunStatus?: AutomationTaskRunStatus | null; +} + +export type AutomationStatusTone = "neutral" | "warning" | "success" | "error"; + +export type AutomationStatusIconKind = + | "queued" + | "success" + | "failed" + | "never-run"; + +export interface AutomationStatusPresentation { + label: string; + tone: AutomationStatusTone; + iconKind: AutomationStatusIconKind; +} + +export function getAutomationStatusPresentation({ + lastRunStatus, + lastTaskRunStatus, +}: AutomationStatusInput): AutomationStatusPresentation | null { + switch (lastTaskRunStatus) { + case "not_started": + case "queued": + return { + label: "Queued", + tone: "warning", + iconKind: "queued", + }; + case "started": + case "in_progress": + return null; + case "completed": + return { + label: "Success", + tone: "success", + iconKind: "success", + }; + case "failed": + case "cancelled": + return { + label: "Failed", + tone: "error", + iconKind: "failed", + }; + default: + break; + } + + switch (lastRunStatus) { + case "running": + return null; + case "success": + return { + label: "Success", + tone: "success", + iconKind: "success", + }; + case "failed": + return { + label: "Failed", + tone: "error", + iconKind: "failed", + }; + default: + return { + label: "Never run", + tone: "neutral", + iconKind: "never-run", + }; + } +} diff --git a/packages/core/src/inbox/artefacts.test.ts b/packages/core/src/inbox/artefacts.test.ts index a4e972613e..3d733ffbd3 100644 --- a/packages/core/src/inbox/artefacts.test.ts +++ b/packages/core/src/inbox/artefacts.test.ts @@ -1,11 +1,43 @@ -import type { SuggestedReviewer } from "@posthog/shared/types"; +import type { + AvailableSuggestedReviewer, + SuggestedReviewer, +} from "@posthog/shared/types"; import { describe, expect, it } from "vitest"; import { + buildReviewerOptions, extractSuggestedReviewers, + orderSuggestedReviewers, reviewerInitials, + reviewerMatchesAvailable, + reviewerOptionLabel, suggestedReviewerDisplayName, + toSuggestedReviewerWriteContent, } from "./artefacts"; +function makeReviewer( + partial: Partial = {}, +): SuggestedReviewer { + return { + github_login: "octocat", + github_name: "The Octocat", + relevant_commits: [], + user: null, + ...partial, + }; +} + +function makeAvailableReviewer( + partial: Partial = {}, +): AvailableSuggestedReviewer { + return { + uuid: "uuid-1", + name: "Ada Lovelace", + email: "ada@example.com", + github_login: "ada", + ...partial, + }; +} + describe("artefacts", () => { it("extracts suggested reviewers from artefacts", () => { const reviewers: SuggestedReviewer[] = [ @@ -46,4 +78,129 @@ describe("artefacts", () => { expect(reviewerInitials("Ben W.", null)).toBe("BW"); expect(reviewerInitials("", "ben@posthog.com")).toBe("BE"); }); + + it("moves the current user to the front", () => { + const reviewers = [ + makeReviewer({ + github_login: "a", + user: { + id: 1, + uuid: "uuid-a", + email: "a@posthog.com", + first_name: "a", + last_name: "", + }, + }), + makeReviewer({ + github_login: "me", + user: { + id: 2, + uuid: "uuid-me", + email: "me@posthog.com", + first_name: "me", + last_name: "", + }, + }), + ]; + + expect( + orderSuggestedReviewers(reviewers, "uuid-me").map( + (reviewer) => reviewer.github_login, + ), + ).toEqual(["me", "a"]); + }); + + it("deduplicates reviewer options and pins the current user first", () => { + const options = buildReviewerOptions( + [ + makeAvailableReviewer({ uuid: "b", name: "Bob" }), + makeAvailableReviewer({ uuid: "a", name: "Ada" }), + makeAvailableReviewer({ uuid: "a", name: "Ada duplicate" }), + ], + "b", + ); + + expect(options.map((option) => option.uuid)).toEqual(["b", "a"]); + }); + + it("labels the current reviewer", () => { + expect( + reviewerOptionLabel({ + uuid: "uuid-me", + name: "Ada", + email: "ada@example.com", + github_login: "ada", + isMe: true, + }), + ).toBe("Ada (Me)"); + }); + + it.each([ + { + name: "user uuid", + reviewer: makeReviewer({ + github_login: "", + user: { + id: 1, + uuid: "uuid-1", + email: "", + first_name: "", + last_name: "", + }, + }), + expected: true, + }, + { + name: "case-insensitive GitHub login", + reviewer: makeReviewer({ github_login: "ADA" }), + expected: true, + }, + { + name: "different reviewer", + reviewer: makeReviewer(), + expected: false, + }, + ])("matches an available reviewer by $name", ({ reviewer, expected }) => { + expect(reviewerMatchesAvailable(reviewer, makeAvailableReviewer())).toBe( + expected, + ); + }); + + it.each([ + { + name: "GitHub login", + reviewer: makeReviewer({ + github_login: "ada", + user: { + id: 1, + uuid: "uuid-1", + email: "", + first_name: "", + last_name: "", + }, + }), + expected: [{ github_login: "ada" }], + }, + { + name: "user uuid fallback", + reviewer: makeReviewer({ + github_login: "", + user: { + id: 1, + uuid: "uuid-1", + email: "", + first_name: "", + last_name: "", + }, + }), + expected: [{ user_uuid: "uuid-1" }], + }, + { + name: "unresolved reviewer", + reviewer: makeReviewer({ github_login: "" }), + expected: [], + }, + ])("builds write content from the $name", ({ reviewer, expected }) => { + expect(toSuggestedReviewerWriteContent([reviewer])).toEqual(expected); + }); }); diff --git a/packages/core/src/inbox/artefacts.ts b/packages/core/src/inbox/artefacts.ts index 1978f710bc..dc5b2eddb2 100644 --- a/packages/core/src/inbox/artefacts.ts +++ b/packages/core/src/inbox/artefacts.ts @@ -1,8 +1,18 @@ import type { + AvailableSuggestedReviewer, RepoSelectionArtefact, SuggestedReviewer, + SuggestedReviewerWriteEntry, } from "@posthog/shared/types"; +export interface ReviewerOption { + uuid: string; + name: string; + email: string; + github_login: string; + isMe: boolean; +} + function hasRepositoryContent( content: unknown, ): content is RepoSelectionArtefact["content"] { @@ -48,6 +58,82 @@ export function extractSuggestedReviewers( return artefact?.content ?? []; } +export function orderSuggestedReviewers( + reviewers: SuggestedReviewer[], + currentUserUuid: string | null | undefined, +): SuggestedReviewer[] { + if (!currentUserUuid) return reviewers; + const currentUserIndex = reviewers.findIndex( + (reviewer) => reviewer.user?.uuid === currentUserUuid, + ); + if (currentUserIndex <= 0) return reviewers; + return [ + reviewers[currentUserIndex], + ...reviewers.filter((_, index) => index !== currentUserIndex), + ]; +} + +export function buildReviewerOptions( + reviewers: AvailableSuggestedReviewer[], + currentUserUuid: string | undefined, +): ReviewerOption[] { + const seen = new Set(); + const options: ReviewerOption[] = []; + + for (const reviewer of reviewers) { + if (!reviewer.uuid || seen.has(reviewer.uuid)) continue; + seen.add(reviewer.uuid); + options.push({ + uuid: reviewer.uuid, + name: reviewer.name?.trim() || "", + email: reviewer.email?.trim() || "", + github_login: reviewer.github_login?.trim() || "", + isMe: reviewer.uuid === currentUserUuid, + }); + } + + options.sort((first, second) => { + if (first.isMe && !second.isMe) return -1; + if (!first.isMe && second.isMe) return 1; + return (first.name || first.email).localeCompare( + second.name || second.email, + ); + }); + + return options; +} + +export function reviewerOptionLabel(reviewer: ReviewerOption): string { + const base = reviewer.name || reviewer.email || "Unknown user"; + return reviewer.isMe ? `${base} (Me)` : base; +} + +export function reviewerMatchesAvailable( + reviewer: SuggestedReviewer, + available: AvailableSuggestedReviewer, +): boolean { + if (reviewer.user?.uuid && reviewer.user.uuid === available.uuid) { + return true; + } + return ( + !!reviewer.github_login && + !!available.github_login && + reviewer.github_login.toLowerCase() === available.github_login.toLowerCase() + ); +} + +export function toSuggestedReviewerWriteContent( + reviewers: SuggestedReviewer[], +): SuggestedReviewerWriteEntry[] { + return reviewers + .map((reviewer): SuggestedReviewerWriteEntry | null => { + if (reviewer.github_login) return { github_login: reviewer.github_login }; + if (reviewer.user?.uuid) return { user_uuid: reviewer.user.uuid }; + return null; + }) + .filter((entry): entry is SuggestedReviewerWriteEntry => entry !== null); +} + const AVATAR_PALETTE = [ "bg-(--orange-9) text-white", "bg-(--blue-9) text-white", diff --git a/packages/core/src/inbox/reportFiltering.test.ts b/packages/core/src/inbox/reportFiltering.test.ts index f2be318fac..3362ca5cde 100644 --- a/packages/core/src/inbox/reportFiltering.test.ts +++ b/packages/core/src/inbox/reportFiltering.test.ts @@ -6,8 +6,18 @@ import { buildSignalReportListOrdering, buildSuggestedReviewerFilterParam, filterReportsBySearch, + INBOX_PIPELINE_STATUS_FILTER, + INBOX_PIPELINE_STATUSES, } from "./reportFiltering"; +describe("inbox pipeline statuses", () => { + it("derives the API filter from the typed status list", () => { + expect(INBOX_PIPELINE_STATUS_FILTER).toBe( + INBOX_PIPELINE_STATUSES.join(","), + ); + }); +}); + function makeReport(overrides: Partial = {}): SignalReport { return { id: "1", diff --git a/packages/core/src/inbox/reportFiltering.ts b/packages/core/src/inbox/reportFiltering.ts index 06d36038b5..2a8271b2cb 100644 --- a/packages/core/src/inbox/reportFiltering.ts +++ b/packages/core/src/inbox/reportFiltering.ts @@ -5,12 +5,20 @@ import type { SignalReportStatus, } from "@posthog/shared/types"; +export const INBOX_PIPELINE_STATUSES = [ + "ready", + "pending_input", + "in_progress", + "failed", + "candidate", + "potential", +] as const satisfies readonly SignalReportStatus[]; + /** * Comma-separated statuses for the inbox query. We pull `failed` so the Runs * tab can surface failed runs in its Recently finished section. */ -export const INBOX_PIPELINE_STATUS_FILTER = - "potential,candidate,in_progress,ready,pending_input,failed"; +export const INBOX_PIPELINE_STATUS_FILTER = INBOX_PIPELINE_STATUSES.join(","); /** * Status filter for the Archive tab — the two terminal, not-in-inbox states: diff --git a/packages/core/src/sessions/cloudSessionConfig.ts b/packages/core/src/sessions/cloudSessionConfig.ts index ecb989b446..b6a9e4f8f4 100644 --- a/packages/core/src/sessions/cloudSessionConfig.ts +++ b/packages/core/src/sessions/cloudSessionConfig.ts @@ -1,6 +1,10 @@ import type { SessionConfigOption } from "@agentclientprotocol/sdk"; import type { Adapter, StoredLogEntry } from "@posthog/shared"; -import { getAvailableCodexModes, getAvailableModes } from "./executionModes"; +import { + DEFAULT_CLAUDE_EXECUTION_MODE, + getAvailableCodexModes, + getAvailableModes, +} from "./executionModes"; /** * Pure derivations of cloud session config options. No store or host access — @@ -54,7 +58,8 @@ export function buildCloudDefaultConfigOptions( ): SessionConfigOption[] { const modes = adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(); - const fallbackMode = adapter === "codex" ? "auto" : "plan"; + const fallbackMode = + adapter === "codex" ? "auto" : DEFAULT_CLAUDE_EXECUTION_MODE; const currentMode = typeof initialMode === "string" && modes.some((mode) => mode.id === initialMode) diff --git a/packages/core/src/sessions/executionModes.ts b/packages/core/src/sessions/executionModes.ts index 4a32413c12..2ccbadf354 100644 --- a/packages/core/src/sessions/executionModes.ts +++ b/packages/core/src/sessions/executionModes.ts @@ -1,4 +1,4 @@ -import { CODEX_MODE_PRESETS } from "@posthog/shared"; +import { CODEX_MODE_PRESETS, type ExecutionMode } from "@posthog/shared"; export interface ModeInfo { id: string; @@ -6,6 +6,8 @@ export interface ModeInfo { description: string; } +export const DEFAULT_CLAUDE_EXECUTION_MODE: ExecutionMode = "plan"; + const availableModes: ModeInfo[] = [ { id: "default", diff --git a/packages/core/src/sessions/portableSessionEvents.test.ts b/packages/core/src/sessions/portableSessionEvents.test.ts new file mode 100644 index 0000000000..d777a0ec8b --- /dev/null +++ b/packages/core/src/sessions/portableSessionEvents.test.ts @@ -0,0 +1,71 @@ +import type { StoredLogEntry } from "@posthog/shared"; +import { describe, expect, it, vi } from "vitest"; +import { + convertStoredEntriesToPortableSessionEvents, + inferStoredLogEntryDirection, +} from "./portableSessionEvents"; + +describe("inferStoredLogEntryDirection", () => { + it.each([ + [ + "client requests", + { notification: { id: 1, method: "session/prompt" } }, + "client", + ], + ["agent responses", { notification: { id: 1, result: {} } }, "agent"], + [ + "agent notifications", + { notification: { method: "session/update" } }, + "agent", + ], + ["missing messages", {}, "agent"], + ] as const)("classifies %s", (_name, entry, expected) => { + expect(inferStoredLogEntryDirection(entry as StoredLogEntry)).toBe( + expected, + ); + }); +}); + +describe("convertStoredEntriesToPortableSessionEvents", () => { + it("projects session updates alongside their raw ACP message", () => { + const notification = { + update: { + sessionUpdate: "user_message_chunk", + content: { type: "text", text: "hello" }, + }, + }; + const events = convertStoredEntriesToPortableSessionEvents([ + { + type: "notification", + timestamp: "2026-07-21T12:00:00.000Z", + notification: { method: "session/update", params: notification }, + }, + ]); + + expect(events).toEqual([ + { + type: "acp_message", + direction: "agent", + ts: 1_784_635_200_000, + message: { method: "session/update", params: notification }, + }, + { + type: "session_update", + ts: 1_784_635_200_000, + notification, + }, + ]); + }); + + it("uses the current time when an entry has no timestamp", () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-07-21T12:00:00.000Z")); + + const events = convertStoredEntriesToPortableSessionEvents([ + { type: "response", notification: { id: 1, result: {} } }, + ]); + + expect(events[0]?.ts).toBe(1_784_635_200_000); + vi.useRealTimers(); + }); +}); diff --git a/packages/core/src/sessions/portableSessionEvents.ts b/packages/core/src/sessions/portableSessionEvents.ts new file mode 100644 index 0000000000..5ee36c7642 --- /dev/null +++ b/packages/core/src/sessions/portableSessionEvents.ts @@ -0,0 +1,98 @@ +import type { JsonRpcMessage, StoredLogEntry } from "@posthog/shared"; + +export type PortableSessionToolCallStatus = + | "pending" + | "in_progress" + | "completed" + | "failed" + | null; + +export interface PortableSessionUpdate { + sessionUpdate?: string; + content?: { type: string; text: string }; + attachments?: Array<{ + kind: "image" | "document"; + uri: string; + fileName: string; + mimeType?: string; + }>; + title?: string; + toolCallId?: string; + status?: PortableSessionToolCallStatus; + rawInput?: Record; + rawOutput?: unknown; + entries?: Array<{ + content: string; + status: "pending" | "in_progress" | "completed" | "failed"; + priority: string; + }>; + _meta?: { + claudeCode?: { + toolName?: string; + parentToolCallId?: string; + }; + }; +} + +export interface PortableSessionNotification { + update?: PortableSessionUpdate; +} + +export interface PortableSessionAcpMessage { + type: "acp_message"; + direction: "client" | "agent"; + ts: number; + message: JsonRpcMessage; +} + +export interface PortableSessionUpdateEvent { + type: "session_update"; + ts: number; + notification: PortableSessionNotification; +} + +export type PortableSessionEvent = + | PortableSessionAcpMessage + | PortableSessionUpdateEvent; + +export function inferStoredLogEntryDirection( + entry: StoredLogEntry, +): "client" | "agent" { + const message = entry.notification; + if (!message) return "agent"; + if (message.id !== undefined && message.method !== undefined) return "client"; + return "agent"; +} + +export function convertStoredEntriesToPortableSessionEvents( + entries: readonly StoredLogEntry[], +): PortableSessionEvent[] { + const events: PortableSessionEvent[] = []; + + for (const entry of entries) { + const ts = entry.timestamp + ? new Date(entry.timestamp).getTime() + : Date.now(); + + events.push({ + type: "acp_message", + direction: inferStoredLogEntryDirection(entry), + ts, + message: (entry.notification ?? {}) as JsonRpcMessage, + }); + + if ( + entry.type === "notification" && + entry.notification?.method === "session/update" && + entry.notification.params + ) { + events.push({ + type: "session_update", + ts, + notification: entry.notification.params as PortableSessionNotification, + }); + } + } + + return events; +} diff --git a/packages/core/src/sessions/sessionActivity.test.ts b/packages/core/src/sessions/sessionActivity.test.ts new file mode 100644 index 0000000000..db795f2118 --- /dev/null +++ b/packages/core/src/sessions/sessionActivity.test.ts @@ -0,0 +1,152 @@ +import { describe, expect, it } from "vitest"; +import type { + PortableSessionEvent, + PortableSessionToolCallStatus, +} from "./portableSessionEvents"; +import { + countUserMessages, + getSessionActivityPhase, + isSessionAwaitingUserInput, +} from "./sessionActivity"; + +function userMessage(ts = 1): PortableSessionEvent { + return { + type: "session_update", + ts, + notification: { + update: { + sessionUpdate: "user_message_chunk", + content: { type: "text", text: "Yes" }, + }, + }, + }; +} + +function questionToolCall( + status: PortableSessionToolCallStatus, + sessionUpdate = "tool_call", +): PortableSessionEvent { + return { + type: "session_update", + ts: 1, + notification: { + update: { + sessionUpdate, + toolCallId: "question-1", + status, + rawInput: { questions: [{ question: "Proceed?", options: [] }] }, + _meta: { claudeCode: { toolName: "AskUserQuestion" } }, + }, + }, + }; +} + +function acpNotification(method: string): PortableSessionEvent { + return { + type: "acp_message", + direction: "agent", + ts: 1, + message: { method }, + }; +} + +describe("isSessionAwaitingUserInput", () => { + it("tracks question tools until a metadata-free completion update", () => { + const completion: PortableSessionEvent = { + type: "session_update", + ts: 2, + notification: { + update: { + sessionUpdate: "tool_call_update", + toolCallId: "question-1", + status: "completed", + }, + }, + }; + + expect( + isSessionAwaitingUserInput([questionToolCall("pending"), completion]), + ).toBe(false); + }); + + it("clears questions when the user responds", () => { + expect( + isSessionAwaitingUserInput([questionToolCall("pending"), userMessage(2)]), + ).toBe(false); + }); + + it("honors explicit waiting and terminal backend markers", () => { + expect( + isSessionAwaitingUserInput([ + acpNotification("_posthog/awaiting_user_input"), + ]), + ).toBe(true); + expect( + isSessionAwaitingUserInput([ + acpNotification("_posthog/awaiting_user_input"), + acpNotification("_posthog/turn_complete"), + ]), + ).toBe(false); + }); +}); + +describe("countUserMessages", () => { + it("counts only projected user message updates", () => { + expect( + countUserMessages([ + userMessage(), + questionToolCall("pending"), + userMessage(2), + ]), + ).toBe(2); + }); +}); + +describe("getSessionActivityPhase", () => { + it.each([ + ["retrying", true, undefined, "connecting"], + [ + "awaiting agent output", + false, + { isPromptPending: true, awaitingAgentOutput: true }, + "connecting", + ], + [ + "working", + false, + { isPromptPending: true, awaitingAgentOutput: false }, + "working", + ], + [ + "not pending", + false, + { isPromptPending: false, awaitingAgentOutput: false }, + "idle", + ], + [ + "terminal", + false, + { + isPromptPending: true, + awaitingAgentOutput: false, + terminalStatus: "completed" as const, + }, + "idle", + ], + [ + "waiting for user", + false, + { + isPromptPending: true, + awaitingAgentOutput: false, + events: [questionToolCall("pending")], + }, + "idle", + ], + ] as const)( + "returns the expected phase while %s", + (_name, retrying, session, expected) => { + expect(getSessionActivityPhase({ retrying, session })).toBe(expected); + }, + ); +}); diff --git a/packages/core/src/sessions/sessionActivity.ts b/packages/core/src/sessions/sessionActivity.ts new file mode 100644 index 0000000000..6e2804ff47 --- /dev/null +++ b/packages/core/src/sessions/sessionActivity.ts @@ -0,0 +1,129 @@ +import { isNotification, POSTHOG_NOTIFICATIONS } from "./acpNotifications"; +import type { + PortableSessionEvent, + PortableSessionNotification, + PortableSessionToolCallStatus, +} from "./portableSessionEvents"; + +export type SessionActivityPhase = "idle" | "connecting" | "working"; + +export interface SessionActivityState { + isPromptPending?: boolean; + awaitingAgentOutput?: boolean; + terminalStatus?: "failed" | "completed"; + events?: readonly PortableSessionEvent[]; +} + +function isQuestionNotification( + notification: PortableSessionNotification, +): boolean { + const update = notification.update; + if (!update) return false; + + const rawToolName = update._meta?.claudeCode?.toolName; + if (typeof rawToolName === "string" && /question/i.test(rawToolName)) { + return true; + } + + const rawInput = update.rawInput; + if (!rawInput) return false; + if (Array.isArray(rawInput.questions)) return true; + + const nestedInput = rawInput.input; + return ( + typeof nestedInput === "object" && + nestedInput !== null && + Array.isArray((nestedInput as { questions?: unknown }).questions) + ); +} + +function isPendingQuestionStatus( + status: PortableSessionToolCallStatus | undefined, +): boolean { + return status === null || status === "pending" || status === "in_progress"; +} + +export function isSessionAwaitingUserInput( + events: readonly PortableSessionEvent[] = [], +): boolean { + let awaitingUserInput = false; + const questionStatuses = new Map< + string, + PortableSessionToolCallStatus | undefined + >(); + + for (const event of events) { + if (event.type === "session_update") { + const update = event.notification.update; + const sessionUpdate = update?.sessionUpdate; + + if (sessionUpdate === "user_message_chunk") { + awaitingUserInput = false; + questionStatuses.clear(); + continue; + } + + if ( + sessionUpdate === "tool_call" || + sessionUpdate === "tool_call_update" + ) { + const toolCallId = update?.toolCallId; + const isKnownQuestion = toolCallId + ? questionStatuses.has(toolCallId) + : false; + if (!isKnownQuestion && !isQuestionNotification(event.notification)) { + continue; + } + + questionStatuses.set( + toolCallId ?? `question-${event.ts}`, + update?.status, + ); + awaitingUserInput = [...questionStatuses.values()].some( + isPendingQuestionStatus, + ); + } + + continue; + } + + const method = "method" in event.message ? event.message.method : undefined; + if (method === "_posthog/awaiting_user_input") { + awaitingUserInput = true; + continue; + } + + if ( + isNotification(method, POSTHOG_NOTIFICATIONS.TURN_COMPLETE) || + isNotification(method, POSTHOG_NOTIFICATIONS.TASK_COMPLETE) || + isNotification(method, POSTHOG_NOTIFICATIONS.ERROR) + ) { + awaitingUserInput = false; + questionStatuses.clear(); + } + } + + return awaitingUserInput; +} + +export function countUserMessages( + events: readonly PortableSessionEvent[] = [], +): number { + return events.filter( + (event) => + event.type === "session_update" && + event.notification.update?.sessionUpdate === "user_message_chunk", + ).length; +} + +export function getSessionActivityPhase(args: { + retrying: boolean; + session?: SessionActivityState | null; +}): SessionActivityPhase { + const { retrying, session } = args; + + if (retrying) return "connecting"; + if (!session?.isPromptPending || session.terminalStatus) return "idle"; + if (isSessionAwaitingUserInput(session.events)) return "idle"; + return session.awaitingAgentOutput ? "connecting" : "working"; +} diff --git a/packages/core/src/tasks/taskActivity.test.ts b/packages/core/src/tasks/taskActivity.test.ts new file mode 100644 index 0000000000..35d1eadaff --- /dev/null +++ b/packages/core/src/tasks/taskActivity.test.ts @@ -0,0 +1,132 @@ +import type { Task } from "@posthog/shared/domain-types"; +import { describe, expect, it } from "vitest"; +import { filterAndSortTasks, taskActivityTimestamp } from "./taskActivity"; + +function makeTask(overrides: Partial = {}): Task { + return { + id: "task-1", + task_number: 1, + slug: "task-1", + title: "A real task", + description: "Do the thing", + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-02T00:00:00Z", + origin_product: "tasks", + ...overrides, + }; +} + +describe("taskActivityTimestamp", () => { + it("uses creation time in created mode", () => { + const task = makeTask({ + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-03T00:00:00Z", + }); + + expect(taskActivityTimestamp(task, "created")).toBe( + new Date("2026-01-01T00:00:00Z").getTime(), + ); + }); + + it("uses the latest task or run update in updated mode", () => { + const task = makeTask({ + updated_at: "2026-01-02T00:00:00Z", + latest_run: { + id: "run-1", + task: "task-1", + team: 1, + branch: null, + status: "completed", + log_url: "", + error_message: null, + output: null, + state: {}, + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-04T00:00:00Z", + completed_at: "2026-01-04T00:00:00Z", + }, + }); + + expect(taskActivityTimestamp(task, "updated")).toBe( + new Date("2026-01-04T00:00:00Z").getTime(), + ); + }); +}); + +describe("filterAndSortTasks", () => { + it.each([ + { title: "", description: "" }, + { title: " ", description: "\n\t" }, + ])("hides contentless placeholder tasks", ({ title, description }) => { + const placeholder = makeTask({ id: "placeholder", title, description }); + const realTask = makeTask({ id: "real" }); + + expect( + filterAndSortTasks([placeholder, realTask], "updated", false, "").map( + (task) => task.id, + ), + ).toEqual(["real"]); + }); + + it("selects internal or external tasks", () => { + const externalTask = makeTask({ id: "external", internal: false }); + const internalTask = makeTask({ id: "internal", internal: true }); + + expect( + filterAndSortTasks( + [externalTask, internalTask], + "updated", + false, + "", + ).map((task) => task.id), + ).toEqual(["external"]); + expect( + filterAndSortTasks([externalTask, internalTask], "updated", true, "").map( + (task) => task.id, + ), + ).toEqual(["internal"]); + }); + + it.each([ + ["title", { title: "Fix Login" }], + ["slug", { slug: "fix-login" }], + ["description", { description: "Fix Login" }], + ] as const)("matches a case-insensitive %s filter", (_field, overrides) => { + const matchingTask = makeTask({ id: "matching", ...overrides }); + const otherTask = makeTask({ id: "other", title: "Unrelated" }); + + expect( + filterAndSortTasks( + [otherTask, matchingTask], + "updated", + false, + "LOGIN", + ).map((task) => task.id), + ).toEqual(["matching"]); + }); + + it("sorts by the selected activity timestamp without mutating input", () => { + const olderCreated = makeTask({ + id: "older-created", + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-04T00:00:00Z", + }); + const newerCreated = makeTask({ + id: "newer-created", + created_at: "2026-01-02T00:00:00Z", + updated_at: "2026-01-03T00:00:00Z", + }); + const tasks = [olderCreated, newerCreated]; + + expect( + filterAndSortTasks(tasks, "created", false, "").map((task) => task.id), + ).toEqual(["newer-created", "older-created"]); + expect( + filterAndSortTasks(tasks, "updated", false, "").map((task) => task.id), + ).toEqual(["older-created", "newer-created"]); + expect(tasks.map((task) => task.id)).toEqual([ + "older-created", + "newer-created", + ]); + }); +}); diff --git a/packages/core/src/tasks/taskActivity.ts b/packages/core/src/tasks/taskActivity.ts new file mode 100644 index 0000000000..6a52168e63 --- /dev/null +++ b/packages/core/src/tasks/taskActivity.ts @@ -0,0 +1,45 @@ +import { isContentlessTask, type Task } from "@posthog/shared/domain-types"; + +export type TaskActivitySortMode = "created" | "updated"; + +export function taskActivityTimestamp( + task: Pick, + sortMode: TaskActivitySortMode, +): number { + if (sortMode === "created") { + return new Date(task.created_at).getTime(); + } + + const runUpdatedAt = task.latest_run?.updated_at; + return Math.max( + runUpdatedAt ? new Date(runUpdatedAt).getTime() : 0, + new Date(task.updated_at ?? task.created_at).getTime(), + ); +} + +export function filterAndSortTasks( + tasks: readonly Task[], + sortMode: TaskActivitySortMode, + showInternal: boolean, + filter: string, +): Task[] { + const normalizedFilter = filter.toLowerCase(); + + return tasks + .filter((task) => !isContentlessTask(task)) + .filter((task) => + showInternal ? task.internal === true : task.internal !== true, + ) + .filter( + (task) => + !normalizedFilter || + task.title.toLowerCase().includes(normalizedFilter) || + task.slug.toLowerCase().includes(normalizedFilter) || + task.description?.toLowerCase().includes(normalizedFilter), + ) + .sort( + (firstTask, secondTask) => + taskActivityTimestamp(secondTask, sortMode) - + taskActivityTimestamp(firstTask, sortMode), + ); +} diff --git a/packages/core/src/tasks/taskArchive.test.ts b/packages/core/src/tasks/taskArchive.test.ts new file mode 100644 index 0000000000..ab22bbd459 --- /dev/null +++ b/packages/core/src/tasks/taskArchive.test.ts @@ -0,0 +1,44 @@ +import type { Task, TaskRunStatus } from "@posthog/shared/domain-types"; +import { describe, expect, it } from "vitest"; +import { isTaskRunning } from "./taskArchive"; + +function makeTask(status?: TaskRunStatus): Pick { + return { + latest_run: status + ? { + id: "run-1", + task: "task-1", + team: 1, + branch: null, + status, + log_url: "", + error_message: null, + output: null, + state: {}, + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-01T00:00:00Z", + completed_at: null, + } + : undefined, + }; +} + +describe("isTaskRunning", () => { + it("returns false when a task has no run", () => { + expect(isTaskRunning(makeTask())).toBe(false); + }); + + it.each(["not_started", "queued", "in_progress"] as const)( + "returns true for %s", + (status) => { + expect(isTaskRunning(makeTask(status))).toBe(true); + }, + ); + + it.each(["completed", "failed", "cancelled"] as const)( + "returns false for %s", + (status) => { + expect(isTaskRunning(makeTask(status))).toBe(false); + }, + ); +}); diff --git a/packages/core/src/tasks/taskArchive.ts b/packages/core/src/tasks/taskArchive.ts new file mode 100644 index 0000000000..db52bb6310 --- /dev/null +++ b/packages/core/src/tasks/taskArchive.ts @@ -0,0 +1,6 @@ +import { isTerminalStatus, type Task } from "@posthog/shared/domain-types"; + +export function isTaskRunning(task: Pick): boolean { + const status = task.latest_run?.status; + return status !== undefined && !isTerminalStatus(status); +} diff --git a/packages/core/src/tasks/taskStatusPresentation.test.ts b/packages/core/src/tasks/taskStatusPresentation.test.ts new file mode 100644 index 0000000000..a7895cf59e --- /dev/null +++ b/packages/core/src/tasks/taskStatusPresentation.test.ts @@ -0,0 +1,69 @@ +import type { Task, TaskRun } from "@posthog/shared/domain-types"; +import { describe, expect, it } from "vitest"; +import { getTaskStatusPresentationKind } from "./taskStatusPresentation"; + +function makeTask(latestRun?: Partial): Pick { + return { + latest_run: latestRun + ? { + id: "run-1", + task: "task-1", + team: 1, + branch: null, + status: "not_started", + log_url: "", + error_message: null, + output: null, + state: {}, + created_at: "2026-01-01T00:00:00Z", + updated_at: "2026-01-01T00:00:00Z", + completed_at: null, + ...latestRun, + } + : undefined, + }; +} + +describe("getTaskStatusPresentationKind", () => { + it("prioritizes a pull request over cloud presentation", () => { + expect( + getTaskStatusPresentationKind( + makeTask({ + environment: "cloud", + status: "in_progress", + output: { pr_url: "https://github.com/PostHog/code/pull/123" }, + }), + ), + ).toBe("pr"); + }); + + it.each([ + "not_started", + "queued", + "in_progress", + "completed", + "failed", + "cancelled", + ] as const)("uses chat presentation for cloud status %s", (status) => { + expect( + getTaskStatusPresentationKind(makeTask({ environment: "cloud", status })), + ).toBe("chat"); + }); + + it.each([ + ["completed", "completed"], + ["failed", "failed"], + ["in_progress", "running"], + ["queued", "started"], + ["not_started", "chat"], + ["cancelled", "chat"], + ] as const)("maps local status %s to %s", (status, expected) => { + expect( + getTaskStatusPresentationKind(makeTask({ environment: "local", status })), + ).toBe(expected); + }); + + it("falls back to chat when a task has no run", () => { + expect(getTaskStatusPresentationKind(makeTask())).toBe("chat"); + }); +}); diff --git a/packages/core/src/tasks/taskStatusPresentation.ts b/packages/core/src/tasks/taskStatusPresentation.ts new file mode 100644 index 0000000000..968a17bb76 --- /dev/null +++ b/packages/core/src/tasks/taskStatusPresentation.ts @@ -0,0 +1,37 @@ +import { readPrUrls } from "@posthog/shared"; +import type { Task } from "@posthog/shared/domain-types"; + +export type TaskStatusPresentationKind = + | "pr" + | "completed" + | "failed" + | "running" + | "started" + | "chat"; + +export function getTaskStatusPresentationKind( + task: Pick, +): TaskStatusPresentationKind { + const latestRun = task.latest_run; + + if (readPrUrls(latestRun?.output)[0]) { + return "pr"; + } + + if (latestRun?.environment === "cloud") { + return "chat"; + } + + switch (latestRun?.status) { + case "completed": + return "completed"; + case "failed": + return "failed"; + case "in_progress": + return "running"; + case "queued": + return "started"; + default: + return "chat"; + } +} From 53e538364092cc252cd383a9c8595a5c72268b8a Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:19:34 +0300 Subject: [PATCH 09/42] refactor(core): rename cloud task service as engine Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/web/src/web-container.ts | 2 +- .../core/src/cloud-task/{cloud-task.ts => cloud-task-engine.ts} | 0 packages/core/src/cloud-task/cloud-task.module.ts | 2 +- packages/core/src/cloud-task/cloud-task.test.ts | 2 +- packages/core/src/handoff/handoff.ts | 2 +- packages/host-router/src/routers/cloud-task.router.ts | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename packages/core/src/cloud-task/{cloud-task.ts => cloud-task-engine.ts} (100%) diff --git a/apps/web/src/web-container.ts b/apps/web/src/web-container.ts index 3075d139ce..cf17cd493e 100644 --- a/apps/web/src/web-container.ts +++ b/apps/web/src/web-container.ts @@ -27,8 +27,8 @@ import { } from "@posthog/core/auth/identifiers"; import { canvasCoreModule } from "@posthog/core/canvas/canvas.module"; import { taskThreadCoreModule } from "@posthog/core/canvas/taskThread.module"; -import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task"; import { cloudTaskModule } from "@posthog/core/cloud-task/cloud-task.module"; +import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task-engine"; import { CLOUD_TASK_AUTH, CLOUD_TASK_SERVICE, diff --git a/packages/core/src/cloud-task/cloud-task.ts b/packages/core/src/cloud-task/cloud-task-engine.ts similarity index 100% rename from packages/core/src/cloud-task/cloud-task.ts rename to packages/core/src/cloud-task/cloud-task-engine.ts diff --git a/packages/core/src/cloud-task/cloud-task.module.ts b/packages/core/src/cloud-task/cloud-task.module.ts index 02f0cc91db..464011d14f 100644 --- a/packages/core/src/cloud-task/cloud-task.module.ts +++ b/packages/core/src/cloud-task/cloud-task.module.ts @@ -1,5 +1,5 @@ import { ContainerModule } from "inversify"; -import { CloudTaskService } from "./cloud-task"; +import { CloudTaskService } from "./cloud-task-engine"; import { CLOUD_TASK_SERVICE } from "./identifiers"; export const cloudTaskModule = new ContainerModule(({ bind }) => { diff --git a/packages/core/src/cloud-task/cloud-task.test.ts b/packages/core/src/cloud-task/cloud-task.test.ts index a7fbf37c66..9002f948ff 100644 --- a/packages/core/src/cloud-task/cloud-task.test.ts +++ b/packages/core/src/cloud-task/cloud-task.test.ts @@ -22,7 +22,7 @@ const fetchRouter = vi.hoisted(() => }), ); -import { CloudTaskService } from "./cloud-task"; +import { CloudTaskService } from "./cloud-task-engine"; const mockAuthService = { authenticatedFetch: vi.fn(), diff --git a/packages/core/src/handoff/handoff.ts b/packages/core/src/handoff/handoff.ts index c85c2fcfa8..2b8d477099 100644 --- a/packages/core/src/handoff/handoff.ts +++ b/packages/core/src/handoff/handoff.ts @@ -5,7 +5,7 @@ import { TypedEventEmitter, } from "@posthog/shared"; import { inject, injectable } from "inversify"; -import type { CloudTaskService } from "../cloud-task/cloud-task"; +import type { CloudTaskService } from "../cloud-task/cloud-task-engine"; import { CLOUD_TASK_SERVICE } from "../cloud-task/identifiers"; import { HandoffSaga, type HandoffSagaDeps } from "./handoff-saga"; import { diff --git a/packages/host-router/src/routers/cloud-task.router.ts b/packages/host-router/src/routers/cloud-task.router.ts index 15d577ce59..4546995404 100644 --- a/packages/host-router/src/routers/cloud-task.router.ts +++ b/packages/host-router/src/routers/cloud-task.router.ts @@ -1,4 +1,4 @@ -import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task"; +import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task-engine"; import { CLOUD_TASK_SERVICE } from "@posthog/core/cloud-task/identifiers"; import { CloudTaskEvent, From 2245a2e61032c11dc77773d4fcd64afc9a121e2a Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:20:17 +0300 Subject: [PATCH 10/42] refactor(core): extract portable cloud task engine Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/web/src/web-container.ts | 2 +- .../core/src/cloud-task/cloud-task-engine.ts | 96 ++++++++++++------- .../src/cloud-task/cloud-task-service.test.ts | 27 ++++++ .../core/src/cloud-task/cloud-task.module.ts | 2 +- .../core/src/cloud-task/cloud-task.test.ts | 47 +++++---- packages/core/src/cloud-task/cloud-task.ts | 35 +++++++ packages/core/src/cloud-task/schemas.ts | 22 ++--- packages/core/src/handoff/handoff.ts | 2 +- packages/core/vitest.config.ts | 23 +++++ .../src/routers/cloud-task.router.ts | 2 +- 10 files changed, 183 insertions(+), 75 deletions(-) create mode 100644 packages/core/src/cloud-task/cloud-task-service.test.ts create mode 100644 packages/core/src/cloud-task/cloud-task.ts create mode 100644 packages/core/vitest.config.ts diff --git a/apps/web/src/web-container.ts b/apps/web/src/web-container.ts index cf17cd493e..3075d139ce 100644 --- a/apps/web/src/web-container.ts +++ b/apps/web/src/web-container.ts @@ -27,8 +27,8 @@ import { } from "@posthog/core/auth/identifiers"; import { canvasCoreModule } from "@posthog/core/canvas/canvas.module"; import { taskThreadCoreModule } from "@posthog/core/canvas/taskThread.module"; +import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task"; import { cloudTaskModule } from "@posthog/core/cloud-task/cloud-task.module"; -import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task-engine"; import { CLOUD_TASK_AUTH, CLOUD_TASK_SERVICE, diff --git a/packages/core/src/cloud-task/cloud-task-engine.ts b/packages/core/src/cloud-task/cloud-task-engine.ts index 22b5a1ddab..006b0ab6d2 100644 --- a/packages/core/src/cloud-task/cloud-task-engine.ts +++ b/packages/core/src/cloud-task/cloud-task-engine.ts @@ -1,37 +1,24 @@ +import type { RootLogger, ScopedLogger } from "@posthog/di/logger"; +import type { IAnalytics } from "@posthog/platform/analytics"; import { - ROOT_LOGGER, - type RootLogger, - type ScopedLogger, -} from "@posthog/di/logger"; -import { - ANALYTICS_SERVICE, - type IAnalytics, -} from "@posthog/platform/analytics"; -import type { StoredLogEntry } from "@posthog/shared"; -import { + type CloudTaskPermissionRequestUpdate, + isTerminalStatus, mcpToolKey, posthogToolMeta, + type StoredLogEntry, serializeError, + type TaskRunStatus, TypedEventEmitter, } from "@posthog/shared"; import { ANALYTICS_EVENTS } from "@posthog/shared/analytics-events"; -import { inject, injectable, optional, preDestroy } from "inversify"; -import type { CloudTaskPermissionRequestUpdate } from "./cloud-task-types"; -import { - CLOUD_TASK_AUTH, - type ICloudTaskAuth, - MCP_RELAY_EXECUTOR, - type McpRelayExecutor, -} from "./identifiers"; +import type { ICloudTaskAuth, McpRelayExecutor } from "./identifiers"; import { CloudTaskEvent, type CloudTaskEvents, - isTerminalStatus, type SendCommandInput, type SendCommandOutput, type StopInput, type StopOutput, - type TaskRunStatus, type WatchInput, } from "./schemas"; import { type SseEvent, SseEventParser } from "./sse-parser"; @@ -435,23 +422,45 @@ function sandboxAlivePayload(watcher: { lastSandboxAlive: boolean | null }): { : { sandboxAlive: watcher.lastSandboxAlive }; } -@injectable() -export class CloudTaskService extends TypedEventEmitter { +export interface CloudTaskEngineDependencies { + auth: ICloudTaskAuth; + analytics: IAnalytics; + logger: RootLogger; + mcpRelayExecutor?: McpRelayExecutor | null; + streamFetch?: CloudTaskFetch; +} + +export type CloudTaskFetch = ( + input: string | URL | Request, + init?: RequestInit, +) => Promise; + +export function createCloudTaskEngine( + dependencies: CloudTaskEngineDependencies, +): CloudTaskEngine { + return new CloudTaskEngine(dependencies); +} + +export class CloudTaskEngine extends TypedEventEmitter { private watchers = new Map(); private readonly log: ScopedLogger; - - constructor( - @inject(CLOUD_TASK_AUTH) - private readonly auth: ICloudTaskAuth, - @inject(ANALYTICS_SERVICE) - private readonly analytics: IAnalytics, - @inject(ROOT_LOGGER) - logger: RootLogger, - @inject(MCP_RELAY_EXECUTOR) - @optional() - private readonly mcpRelayExecutor: McpRelayExecutor | null = null, - ) { + private readonly auth: ICloudTaskAuth; + private readonly analytics: IAnalytics; + private readonly mcpRelayExecutor: McpRelayExecutor | null; + private readonly streamFetch: CloudTaskFetch; + + constructor({ + auth, + analytics, + logger, + mcpRelayExecutor = null, + streamFetch = globalThis.fetch.bind(globalThis), + }: CloudTaskEngineDependencies) { super(); + this.auth = auth; + this.analytics = analytics; + this.mcpRelayExecutor = mcpRelayExecutor; + this.streamFetch = streamFetch; this.log = logger.scope("cloud-task"); } @@ -770,6 +779,22 @@ export class CloudTaskService extends TypedEventEmitter { void this.bootstrapWatcher(key); } + reconnectIfDisconnected(taskId: string, runId: string): void { + const key = watcherKey(taskId, runId); + const watcher = this.watchers.get(key); + if ( + !watcher || + watcher.sseAbortController || + watcher.reconnectTimeoutId || + watcher.isBootstrapping || + isTerminalStatus(watcher.lastStatus) + ) { + return; + } + + void this.connectSse(key); + } + // Resets a watcher to its pre-bootstrap state so bootstrapWatcher can rebuild it from server truth. private resetWatcherForRebootstrap(watcher: WatcherState): void { watcher.reconnectAttempts = 0; @@ -959,7 +984,6 @@ export class CloudTaskService extends TypedEventEmitter { } } - @preDestroy() unwatchAll(): void { for (const key of [...this.watchers.keys()]) { this.stopWatcher(key); @@ -1306,7 +1330,7 @@ export class CloudTaskService extends TypedEventEmitter { try { // The proxy authenticates with the run-scoped Bearer token; the Django leg uses the session. const response = usingProxy - ? await fetch(url.toString(), { + ? await this.streamFetch(url.toString(), { method: "GET", headers, signal: controller.signal, diff --git a/packages/core/src/cloud-task/cloud-task-service.test.ts b/packages/core/src/cloud-task/cloud-task-service.test.ts new file mode 100644 index 0000000000..644f9dd7cb --- /dev/null +++ b/packages/core/src/cloud-task/cloud-task-service.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it, vi } from "vitest"; +import { CloudTaskService } from "./cloud-task"; +import { CloudTaskEngine } from "./cloud-task-engine"; + +describe("CloudTaskService", () => { + it("preserves the injectable service API as a thin engine wrapper", () => { + const scopedLog = { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + }; + const service = new CloudTaskService( + { + authenticatedFetch: vi.fn(), + getCloudContext: vi.fn(), + }, + { track: vi.fn() } as never, + { ...scopedLog, scope: vi.fn(() => scopedLog) }, + ); + + expect(service).toBeInstanceOf(CloudTaskEngine); + expect(service.watch).toBeTypeOf("function"); + expect(service.retry).toBeTypeOf("function"); + expect(service.unwatchAll).toBeTypeOf("function"); + }); +}); diff --git a/packages/core/src/cloud-task/cloud-task.module.ts b/packages/core/src/cloud-task/cloud-task.module.ts index 464011d14f..02f0cc91db 100644 --- a/packages/core/src/cloud-task/cloud-task.module.ts +++ b/packages/core/src/cloud-task/cloud-task.module.ts @@ -1,5 +1,5 @@ import { ContainerModule } from "inversify"; -import { CloudTaskService } from "./cloud-task-engine"; +import { CloudTaskService } from "./cloud-task"; import { CLOUD_TASK_SERVICE } from "./identifiers"; export const cloudTaskModule = new ContainerModule(({ bind }) => { diff --git a/packages/core/src/cloud-task/cloud-task.test.ts b/packages/core/src/cloud-task/cloud-task.test.ts index 9002f948ff..4178bd3d78 100644 --- a/packages/core/src/cloud-task/cloud-task.test.ts +++ b/packages/core/src/cloud-task/cloud-task.test.ts @@ -5,14 +5,17 @@ const mockNetFetch = vi.hoisted(() => vi.fn()); const mockStreamFetch = vi.hoisted(() => vi.fn()); const mockStreamTokenFetch = vi.hoisted(() => vi.fn()); -// The service now uses global fetch for BOTH authenticated API calls (JSON) -// and SSE streaming. The two used to be distinct (net.fetch vs global fetch). // Route by URL: /stream_token/ → token mock (read-leg resolution), the stream leg // (Django /stream/ or proxy /v1/runs/:run/stream) → stream mock, everything else → API mock. // The token mock has a Django-path default so existing fixtures (which never set it) are untouched. const fetchRouter = vi.hoisted(() => - vi.fn((input: string | Request, init?: RequestInit) => { - const url = typeof input === "string" ? input : input.url; + vi.fn((input: string | URL | Request, init?: RequestInit) => { + const url = + typeof input === "string" + ? input + : input instanceof URL + ? input.toString() + : input.url; const impl = url.includes("/stream_token/") ? mockStreamTokenFetch : /\/stream(\/|\?|$)/.test(url) @@ -22,7 +25,10 @@ const fetchRouter = vi.hoisted(() => }), ); -import { CloudTaskService } from "./cloud-task-engine"; +import { + type CloudTaskEngine, + createCloudTaskEngine, +} from "./cloud-task-engine"; const mockAuthService = { authenticatedFetch: vi.fn(), @@ -86,8 +92,8 @@ async function waitFor( } } -describe("CloudTaskService", () => { - let service: CloudTaskService; +describe("CloudTaskEngine", () => { + let service: CloudTaskEngine; beforeEach(() => { const scopedLog = { @@ -98,11 +104,12 @@ describe("CloudTaskService", () => { }; const loggerMock = { ...scopedLog, scope: vi.fn(() => scopedLog) }; const analyticsMock = { track: vi.fn() }; - service = new CloudTaskService( - mockAuthService as never, - analyticsMock as never, - loggerMock, - ); + service = createCloudTaskEngine({ + auth: mockAuthService as never, + analytics: analyticsMock as never, + logger: loggerMock, + streamFetch: fetchRouter, + }); mockNetFetch.mockReset(); mockStreamFetch.mockReset(); mockStreamTokenFetch.mockReset(); @@ -3077,8 +3084,8 @@ describe("CloudTaskService", () => { }); }); -describe("CloudTaskService MCP relay", () => { - let relayService: CloudTaskService; +describe("CloudTaskEngine MCP relay", () => { + let relayService: CloudTaskEngine; let mcpRelayExecutor: { execute: ReturnType; closeRun: ReturnType; @@ -3099,12 +3106,12 @@ describe("CloudTaskService MCP relay", () => { })), closeRun: vi.fn(async () => {}), }; - relayService = new CloudTaskService( - mockAuthService as never, - analyticsMock as never, - loggerMock, - mcpRelayExecutor as never, - ); + relayService = createCloudTaskEngine({ + auth: mockAuthService as never, + analytics: analyticsMock as never, + logger: loggerMock, + mcpRelayExecutor: mcpRelayExecutor as never, + }); mockNetFetch.mockReset(); mockStreamFetch.mockReset(); diff --git a/packages/core/src/cloud-task/cloud-task.ts b/packages/core/src/cloud-task/cloud-task.ts new file mode 100644 index 0000000000..1e2003d85a --- /dev/null +++ b/packages/core/src/cloud-task/cloud-task.ts @@ -0,0 +1,35 @@ +import { ROOT_LOGGER, type RootLogger } from "@posthog/di/logger"; +import { + ANALYTICS_SERVICE, + type IAnalytics, +} from "@posthog/platform/analytics"; +import { inject, injectable, optional, preDestroy } from "inversify"; +import { CloudTaskEngine } from "./cloud-task-engine"; +import { + CLOUD_TASK_AUTH, + type ICloudTaskAuth, + MCP_RELAY_EXECUTOR, + type McpRelayExecutor, +} from "./identifiers"; + +@injectable() +export class CloudTaskService extends CloudTaskEngine { + constructor( + @inject(CLOUD_TASK_AUTH) + auth: ICloudTaskAuth, + @inject(ANALYTICS_SERVICE) + analytics: IAnalytics, + @inject(ROOT_LOGGER) + logger: RootLogger, + @inject(MCP_RELAY_EXECUTOR) + @optional() + mcpRelayExecutor: McpRelayExecutor | null = null, + ) { + super({ auth, analytics, logger, mcpRelayExecutor }); + } + + @preDestroy() + override unwatchAll(): void { + super.unwatchAll(); + } +} diff --git a/packages/core/src/cloud-task/schemas.ts b/packages/core/src/cloud-task/schemas.ts index d694e52141..b8c03eb202 100644 --- a/packages/core/src/cloud-task/schemas.ts +++ b/packages/core/src/cloud-task/schemas.ts @@ -1,20 +1,12 @@ -import type { TaskRunStatus } from "@posthog/shared"; +import type { CloudTaskUpdatePayload } from "@posthog/shared"; import { z } from "zod"; -import type { CloudTaskUpdatePayload } from "./cloud-task-types"; -export type { CloudTaskUpdatePayload, TaskRunStatus }; - -export const TERMINAL_STATUSES = ["completed", "failed", "cancelled"] as const; - -export function isTerminalStatus( - status: TaskRunStatus | string | null | undefined, -): boolean { - return ( - status !== null && - status !== undefined && - TERMINAL_STATUSES.includes(status as (typeof TERMINAL_STATUSES)[number]) - ); -} +export { + type CloudTaskUpdatePayload, + isTerminalStatus, + type TaskRunStatus, + TERMINAL_STATUSES, +} from "@posthog/shared"; // --- Events --- diff --git a/packages/core/src/handoff/handoff.ts b/packages/core/src/handoff/handoff.ts index 2b8d477099..c85c2fcfa8 100644 --- a/packages/core/src/handoff/handoff.ts +++ b/packages/core/src/handoff/handoff.ts @@ -5,7 +5,7 @@ import { TypedEventEmitter, } from "@posthog/shared"; import { inject, injectable } from "inversify"; -import type { CloudTaskService } from "../cloud-task/cloud-task-engine"; +import type { CloudTaskService } from "../cloud-task/cloud-task"; import { CLOUD_TASK_SERVICE } from "../cloud-task/identifiers"; import { HandoffSaga, type HandoffSagaDeps } from "./handoff-saga"; import { diff --git a/packages/core/vitest.config.ts b/packages/core/vitest.config.ts new file mode 100644 index 0000000000..bed14b24e0 --- /dev/null +++ b/packages/core/vitest.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from "vitest/config"; +import { trunkTestOptions } from "../../vitest.config.base"; + +export default defineConfig({ + oxc: false, + esbuild: { + tsconfigRaw: { + compilerOptions: { + experimentalDecorators: true, + target: "ES2022", + useDefineForClassFields: false, + verbatimModuleSyntax: true, + }, + }, + }, + test: { + globals: true, + ...trunkTestOptions, + environment: "node", + include: ["src/**/*.test.ts", "src/**/*.test.tsx"], + exclude: ["**/node_modules/**", "**/dist/**"], + }, +}); diff --git a/packages/host-router/src/routers/cloud-task.router.ts b/packages/host-router/src/routers/cloud-task.router.ts index 4546995404..15d577ce59 100644 --- a/packages/host-router/src/routers/cloud-task.router.ts +++ b/packages/host-router/src/routers/cloud-task.router.ts @@ -1,4 +1,4 @@ -import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task-engine"; +import type { CloudTaskService } from "@posthog/core/cloud-task/cloud-task"; import { CLOUD_TASK_SERVICE } from "@posthog/core/cloud-task/identifiers"; import { CloudTaskEvent, From 36f9b398e6afb14a921e49148574f39f104a66c5 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:21:03 +0300 Subject: [PATCH 11/42] refactor(core): extract repository integration semantics Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../src/integrations/repositories.test.ts | 66 ++++++++++ .../core/src/integrations/repositories.ts | 124 ++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/packages/core/src/integrations/repositories.test.ts b/packages/core/src/integrations/repositories.test.ts index ce6af19540..80eec8ad36 100644 --- a/packages/core/src/integrations/repositories.test.ts +++ b/packages/core/src/integrations/repositories.test.ts @@ -1,5 +1,7 @@ import { describe, expect, it } from "vitest"; import { + buildTeamRepositoryOptions, + buildUserRepositoryOptions, combineGithubRepositories, combineRepositoryPicker, combineUserGithubRepositories, @@ -7,8 +9,11 @@ import { isEmptyRepositoryMap, isRepoInIntegration, normalizeRepoKey, + normalizeRepositoryNames, type RepositoryCacheAction, type RepositoryQueryResult, + repositoryLoadWarning, + repositoryOptionsEqual, resolveEffectiveUserRepositoryMap, resolveUserRepositoryCacheAction, sameUserRepositoryMap, @@ -18,6 +23,67 @@ import { type UserRepositoryIntegrationRef, } from "./repositories"; +describe("repository options", () => { + it("normalizes repository names", () => { + expect(normalizeRepositoryNames(["PostHog/Code", ""])).toEqual([ + "posthog/code", + ]); + }); + + it("builds sorted team options with integration labels", () => { + expect( + buildTeamRepositoryOptions( + [ + { id: 2, display_name: "Work" }, + { id: 1, config: { account: { login: "personal" } } }, + ], + { 1: ["z/repo"], 2: ["a/repo"] }, + ), + ).toEqual([ + { integrationId: 2, integrationLabel: "Work", repository: "a/repo" }, + { + integrationId: 1, + integrationLabel: "personal", + repository: "z/repo", + }, + ]); + }); + + it("builds user options with the same shape", () => { + expect( + buildUserRepositoryOptions( + [{ id: "user-1", installation_id: "42", account: { name: "Me" } }], + { 42: ["posthog/code"] }, + ), + ).toEqual([ + { integrationId: 42, integrationLabel: "Me", repository: "posthog/code" }, + ]); + }); + + it.each([ + [0, 2, null], + [1, 2, "Some GitHub repositories could not be loaded. Pull to retry."], + [2, 2, "Could not load GitHub repositories. Pull to retry."], + ])( + "describes %i of %i failed repository loads", + (failed, total, expected) => { + expect(repositoryLoadWarning(failed, total)).toBe(expected); + }, + ); + + it("compares option lists by content", () => { + const options = [ + { integrationId: 1, integrationLabel: "Me", repository: "a/repo" }, + ]; + expect( + repositoryOptionsEqual( + options, + options.map((option) => ({ ...option })), + ), + ).toBe(true); + }); +}); + function result( data: T | undefined, flags: Partial, "data">> = {}, diff --git a/packages/core/src/integrations/repositories.ts b/packages/core/src/integrations/repositories.ts index 43a7fbf74e..ea9bb6d202 100644 --- a/packages/core/src/integrations/repositories.ts +++ b/packages/core/src/integrations/repositories.ts @@ -5,6 +5,130 @@ export interface RepositoryQueryResult { isRefetching: boolean; } +export interface RepositoryOption { + integrationId: number; + integrationLabel: string; + repository: string; +} + +export interface RepositorySelection { + integrationId: number | null; + repository: string | null; +} + +export interface TeamRepositoryIntegration { + id: number; + display_name?: string; + config?: { account?: { login?: string } }; +} + +export interface UserRepositoryIntegration { + id: string; + installation_id: string; + account?: { name?: string | null } | null; +} + +export function normalizeRepositoryNames( + repositories: ReadonlyArray, +): string[] { + return repositories + .map((repository) => repository.toLowerCase()) + .filter((repository) => repository.length > 0); +} + +export function repositoryLoadWarning( + failedCount: number, + totalCount: number, +): string | null { + if (failedCount === 0) return null; + return failedCount === totalCount + ? "Could not load GitHub repositories. Pull to retry." + : "Some GitHub repositories could not be loaded. Pull to retry."; +} + +export function buildTeamRepositoryOptions( + integrations: ReadonlyArray, + repositoriesByIntegration: Readonly>, +): RepositoryOption[] { + return integrations + .flatMap((integration) => + (repositoriesByIntegration[integration.id] ?? []).map((repository) => ({ + integrationId: integration.id, + integrationLabel: + integration.display_name ?? + integration.config?.account?.login ?? + `GitHub ${integration.id}`, + repository, + })), + ) + .sort((left, right) => left.repository.localeCompare(right.repository)); +} + +export function buildUserRepositoryOptions( + integrations: ReadonlyArray, + repositoriesByInstallation: Readonly>, +): RepositoryOption[] { + return integrations + .flatMap((integration) => + (repositoriesByInstallation[integration.installation_id] ?? []).map( + (repository) => ({ + integrationId: Number(integration.installation_id), + integrationLabel: + integration.account?.name ?? + `GitHub ${integration.installation_id}`, + repository, + }), + ), + ) + .sort((left, right) => left.repository.localeCompare(right.repository)); +} + +export function repositoryOptionsEqual( + left: ReadonlyArray, + right: ReadonlyArray, +): boolean { + return ( + left.length === right.length && + left.every((option, index) => { + const other = right[index]; + return ( + other?.integrationId === option.integrationId && + other.integrationLabel === option.integrationLabel && + other.repository === option.repository + ); + }) + ); +} + +export function findRepositoryOption( + options: ReadonlyArray, + selection: RepositorySelection, +): RepositoryOption | null { + if (!selection.integrationId || !selection.repository) return null; + return ( + options.find( + (option) => + option.integrationId === selection.integrationId && + option.repository === selection.repository, + ) ?? null + ); +} + +export function toRepositorySelection( + option: RepositoryOption | null, +): RepositorySelection { + return { + integrationId: option?.integrationId ?? null, + repository: option?.repository ?? null, + }; +} + +export function isRepositorySelectionComplete( + selection: RepositorySelection, +): boolean { + return !!selection.integrationId && !!selection.repository; +} + export interface TeamRepositoriesResult { integrationId: number; repos?: string[] | null; From 3003336e7f5c74b0181f0f79a4ec92524c372071 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:21:33 +0300 Subject: [PATCH 12/42] refactor(core): extract pending prompt recovery Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../core/src/tasks/pendingPrompts.test.ts | 37 +++++++++++++++ packages/core/src/tasks/pendingPrompts.ts | 47 +++++++++++++++++++ .../task-detail/hooks/useTaskCreation.ts | 7 ++- .../ui/src/shell/pendingTaskPromptStore.ts | 45 +++++++----------- 4 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 packages/core/src/tasks/pendingPrompts.test.ts create mode 100644 packages/core/src/tasks/pendingPrompts.ts diff --git a/packages/core/src/tasks/pendingPrompts.test.ts b/packages/core/src/tasks/pendingPrompts.test.ts new file mode 100644 index 0000000000..8a733ad113 --- /dev/null +++ b/packages/core/src/tasks/pendingPrompts.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; +import { + buildPendingPromptKey, + capPendingPrompts, + listPendingPromptsNewestFirst, + selectNewestPendingPrompt, +} from "./pendingPrompts"; + +describe("pending prompts", () => { + it("keeps the newest prompts up to the limit", () => { + expect( + capPendingPrompts( + { + old: { createdAt: 1 }, + middle: { createdAt: 2 }, + newest: { createdAt: 3 }, + }, + 2, + ), + ).toEqual({ middle: { createdAt: 2 }, newest: { createdAt: 3 } }); + }); + + it("orders prompts newest first and selects the newest", () => { + const prompts = { old: { createdAt: 1 }, new: { createdAt: 2 } }; + expect( + listPendingPromptsNewestFirst(prompts).map(({ key }) => key), + ).toEqual(["new", "old"]); + expect(selectNewestPendingPrompt(prompts)?.key).toBe("new"); + }); + + it.each([ + ["uuid", 1, "abc", "uuid"], + [null, 123, "abc", "pending-123-abc"], + ])("builds a portable pending key", (uuid, timestamp, entropy, expected) => { + expect(buildPendingPromptKey(uuid, timestamp, entropy)).toBe(expected); + }); +}); diff --git a/packages/core/src/tasks/pendingPrompts.ts b/packages/core/src/tasks/pendingPrompts.ts new file mode 100644 index 0000000000..e4f77eafa2 --- /dev/null +++ b/packages/core/src/tasks/pendingPrompts.ts @@ -0,0 +1,47 @@ +export const MAX_RECOVERABLE_PROMPTS = 20; + +export interface TimestampedPendingPrompt { + createdAt: number; +} + +export interface RecoverablePendingPrompt< + TPrompt extends TimestampedPendingPrompt, +> { + key: string; + prompt: TPrompt; +} + +export function capPendingPrompts( + byKey: Record, + limit: number = MAX_RECOVERABLE_PROMPTS, +): Record { + const keys = Object.keys(byKey); + if (keys.length <= limit) return byKey; + + const kept = keys + .sort((left, right) => byKey[right].createdAt - byKey[left].createdAt) + .slice(0, limit); + return Object.fromEntries(kept.map((key) => [key, byKey[key]])); +} + +export function listPendingPromptsNewestFirst< + TPrompt extends TimestampedPendingPrompt, +>(byKey: Record): RecoverablePendingPrompt[] { + return Object.entries(byKey) + .map(([key, prompt]) => ({ key, prompt })) + .sort((left, right) => right.prompt.createdAt - left.prompt.createdAt); +} + +export function selectNewestPendingPrompt< + TPrompt extends TimestampedPendingPrompt, +>(byKey: Record): RecoverablePendingPrompt | null { + return listPendingPromptsNewestFirst(byKey)[0] ?? null; +} + +export function buildPendingPromptKey( + randomUuid: string | null, + timestamp: number, + entropy: string, +): string { + return randomUuid ?? `pending-${timestamp}-${entropy}`; +} diff --git a/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts b/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts index d13a1c445f..9b3abdaae0 100644 --- a/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts +++ b/packages/ui/src/features/task-detail/hooks/useTaskCreation.ts @@ -31,7 +31,10 @@ import { useConnectivity } from "../../../hooks/useConnectivity"; import { toast } from "../../../primitives/toast"; import { track } from "../../../shell/analytics"; import { logger } from "../../../shell/logger"; -import { pendingTaskPromptStoreApi } from "../../../shell/pendingTaskPromptStore"; +import { + generatePendingTaskKey, + pendingTaskPromptStoreApi, +} from "../../../shell/pendingTaskPromptStore"; import { titleAttachmentStoreApi } from "../../../shell/titleAttachmentStore"; import { useAuthStateValue } from "../../auth/store"; import { assertCloudUsageAvailable } from "../../billing/preflightCloudUsage"; @@ -319,7 +322,7 @@ export function useTaskCreation({ const shouldShowPendingView = !onTaskCreated && !!plainPromptText; const pendingTaskKey = shouldShowPendingView - ? (globalThis.crypto?.randomUUID?.() ?? `pending-${Date.now()}`) + ? generatePendingTaskKey() : null; if (pendingTaskKey) { diff --git a/packages/ui/src/shell/pendingTaskPromptStore.ts b/packages/ui/src/shell/pendingTaskPromptStore.ts index b91fefd3f4..cd227ef24a 100644 --- a/packages/ui/src/shell/pendingTaskPromptStore.ts +++ b/packages/ui/src/shell/pendingTaskPromptStore.ts @@ -1,13 +1,8 @@ import type { UserMessageAttachment } from "@posthog/ui/features/sessions/userMessageTypes"; -import { logger } from "@posthog/ui/shell/logger"; import { electronStorage } from "@posthog/ui/shell/rendererStorage"; import { create } from "zustand"; import { persist } from "zustand/middleware"; -const log = logger.scope("pending-task-prompts"); - -const MAX_PENDING_PROMPTS = 20; - export interface PendingTaskPrompt { promptText: string; attachments: UserMessageAttachment[]; @@ -16,26 +11,6 @@ export interface PendingTaskPrompt { export type PendingTaskPromptInput = Omit; -function capToNewest( - byKey: Record, -): Record { - const keys = Object.keys(byKey); - if (keys.length <= MAX_PENDING_PROMPTS) { - return byKey; - } - const keptKeys = keys - .sort((a, b) => byKey[b].createdAt - byKey[a].createdAt) - .slice(0, MAX_PENDING_PROMPTS); - log.warn("Dropping oldest unrecovered prompts beyond cap", { - dropped: keys.length - keptKeys.length, - }); - const kept: Record = {}; - for (const key of keptKeys) { - kept[key] = byKey[key]; - } - return kept; -} - interface PendingTaskPromptStore { byKey: Record; _hasHydrated: boolean; @@ -54,7 +29,7 @@ export const usePendingTaskPromptStore = create()( setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }), set: (key, prompt) => set((state) => ({ - byKey: capToNewest({ + byKey: capPendingPrompts({ ...state.byKey, [key]: { ...prompt, createdAt: Date.now() }, }), @@ -110,9 +85,7 @@ export const pendingTaskPromptStoreApi = { usePendingTaskPromptStore.getState().move(fromKey, toKey), clear: (key: string) => usePendingTaskPromptStore.getState().clear(key), getAllNewestFirst: (): RecoverablePendingPrompt[] => - Object.entries(usePendingTaskPromptStore.getState().byKey) - .map(([key, prompt]) => ({ key, prompt })) - .sort((a, b) => b.prompt.createdAt - a.prompt.createdAt), + listPendingPromptsNewestFirst(usePendingTaskPromptStore.getState().byKey), whenHydrated: (): Promise => { if (usePendingTaskPromptStore.getState()._hasHydrated) { return Promise.resolve(); @@ -128,6 +101,14 @@ export const pendingTaskPromptStoreApi = { }, }; +export function generatePendingTaskKey(): string { + return buildPendingPromptKey( + globalThis.crypto?.randomUUID?.() ?? null, + Date.now(), + Math.random().toString(36).slice(2, 10), + ); +} + export function usePendingTaskPrompt( key: string | undefined, ): PendingTaskPrompt | undefined { @@ -135,3 +116,9 @@ export function usePendingTaskPrompt( key ? state.byKey[key] : undefined, ); } + +import { + buildPendingPromptKey, + capPendingPrompts, + listPendingPromptsNewestFirst, +} from "@posthog/core/tasks/pendingPrompts"; From b5204c8a1322c09f3b1f9a6432529a65479d787f Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:22:05 +0300 Subject: [PATCH 13/42] refactor(core): extract plan approval presentation Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../sessions/planApprovalPresentation.test.ts | 29 +++++++++++++++++++ .../src/sessions/planApprovalPresentation.ts | 23 +++++++++++++++ .../session-update/PlanApprovalView.tsx | 19 ++++-------- 3 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 packages/core/src/sessions/planApprovalPresentation.test.ts create mode 100644 packages/core/src/sessions/planApprovalPresentation.ts diff --git a/packages/core/src/sessions/planApprovalPresentation.test.ts b/packages/core/src/sessions/planApprovalPresentation.test.ts new file mode 100644 index 0000000000..27f9636189 --- /dev/null +++ b/packages/core/src/sessions/planApprovalPresentation.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from "vitest"; +import { extractPlanText } from "./planApprovalPresentation"; + +describe("extractPlanText", () => { + it.each([ + [{ rawInput: { plan: "Raw plan" } }, "Raw plan"], + [{ content: [{ text: "Direct content" }] }, "Direct content"], + [ + { + content: [ + { type: "content", content: { type: "text", text: "Nested" } }, + ], + }, + "Nested", + ], + [{ rawInput: {}, content: [] }, null], + ])("extracts plan presentation from %o", (toolCall, expected) => { + expect(extractPlanText(toolCall)).toBe(expected); + }); + + it("prefers the canonical raw plan over rendered content", () => { + expect( + extractPlanText({ + rawInput: { plan: "Canonical" }, + content: [{ text: "Rendered" }], + }), + ).toBe("Canonical"); + }); +}); diff --git a/packages/core/src/sessions/planApprovalPresentation.ts b/packages/core/src/sessions/planApprovalPresentation.ts new file mode 100644 index 0000000000..2dd43553b6 --- /dev/null +++ b/packages/core/src/sessions/planApprovalPresentation.ts @@ -0,0 +1,23 @@ +function extractTextContent(item: unknown): string | null { + if (!item || typeof item !== "object") return null; + const record = item as Record; + if (typeof record.text === "string") return record.text; + + if (!record.content || typeof record.content !== "object") return null; + const content = record.content as Record; + return typeof content.text === "string" ? content.text : null; +} + +export function extractPlanText(toolCall: { + rawInput?: { plan?: unknown } | null; + content?: readonly unknown[] | null; +}): string | null { + const rawPlan = toolCall.rawInput?.plan; + if (typeof rawPlan === "string" && rawPlan.trim()) return rawPlan; + + for (const item of toolCall.content ?? []) { + const text = extractTextContent(item); + if (text?.trim()) return text; + } + return null; +} diff --git a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.tsx b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.tsx index b70c281953..bf609d89d2 100644 --- a/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.tsx +++ b/packages/ui/src/features/sessions/components/session-update/PlanApprovalView.tsx @@ -1,4 +1,5 @@ import { CaretDown, CaretRight, CheckCircle } from "@phosphor-icons/react"; +import { extractPlanText } from "@posthog/core/sessions/planApprovalPresentation"; import { Box, Flex, Text } from "@radix-ui/themes"; import { useMemo, useState } from "react"; import { PlanContent } from "../../../permissions/PlanContent"; @@ -32,20 +33,10 @@ export function PlanApprovalView({ | undefined; const isHistoricalPlan = rawInput?.historical === true; - const planText = useMemo(() => { - if (content?.length) { - const textContent = content.find((c) => c.type === "content"); - if (textContent && "content" in textContent) { - const inner = textContent.content as - | { type?: string; text?: string } - | undefined; - if (inner?.type === "text" && inner.text) { - return inner.text; - } - } - } - return rawInput?.plan ?? null; - }, [content, rawInput?.plan]); + const planText = useMemo( + () => extractPlanText({ rawInput, content }), + [content, rawInput], + ); const wasNotApproved = isFailed || wasCancelled; const showResult = isHistoricalPlan || isComplete || wasNotApproved; From 7ccd2592d6cc8f4117fe8287442ee751aeeade10 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:22:25 +0300 Subject: [PATCH 14/42] refactor(core): extract permission option presentation Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../src/sessions/permissionResponse.test.ts | 52 ++++++++++++++ .../core/src/sessions/permissionResponse.ts | 67 +++++++++++++++++++ .../permissions/PlanApprovalSelector.tsx | 42 +++--------- packages/ui/src/features/permissions/types.ts | 9 ++- 4 files changed, 131 insertions(+), 39 deletions(-) diff --git a/packages/core/src/sessions/permissionResponse.test.ts b/packages/core/src/sessions/permissionResponse.test.ts index 228a28142d..d467ca6154 100644 --- a/packages/core/src/sessions/permissionResponse.test.ts +++ b/packages/core/src/sessions/permissionResponse.test.ts @@ -2,10 +2,62 @@ import type { PermissionRequest } from "@posthog/shared"; import { describe, expect, it } from "vitest"; import { formatPermissionAnswerPrompt, + getPermissionOptionMeta, isOtherPermissionOption, + isPermissionApproval, + isPermissionRejection, + permissionOptionUsesCustomInput, planPermissionResponse, + resolveInitialPlanApprovalOption, + selectPlanPermissionOptions, } from "./permissionResponse"; +describe("permission option presentation", () => { + const approveOnce = { + optionId: "default", + name: "Approve", + kind: "allow_once" as const, + }; + const approveAuto = { + optionId: "auto", + name: "Approve automatically", + kind: "allow_always" as const, + }; + const reject = { + optionId: "reject_with_feedback", + name: "Reject", + kind: "reject_once" as const, + _meta: { customInput: true, description: "Explain why" }, + }; + + it("classifies approval, rejection, and custom-input options", () => { + expect(isPermissionApproval(approveOnce)).toBe(true); + expect(isPermissionRejection(reject)).toBe(true); + expect(permissionOptionUsesCustomInput(reject)).toBe(true); + expect(getPermissionOptionMeta(reject)).toEqual({ + customInput: true, + description: "Explain why", + }); + }); + + it("selects plan options and prefers a feedback rejection", () => { + expect(selectPlanPermissionOptions([approveOnce, reject])).toEqual({ + approvals: [approveOnce], + rejection: reject, + }); + }); + + it.each([ + ["default", "default"], + [null, "auto"], + ["missing", "auto"], + ])("resolves preferred approval %s", (preferred, expected) => { + expect( + resolveInitialPlanApprovalOption([approveOnce, approveAuto], preferred), + ).toBe(expected); + }); +}); + function makePermission( options: Array<{ optionId: string; diff --git a/packages/core/src/sessions/permissionResponse.ts b/packages/core/src/sessions/permissionResponse.ts index eef81a7f10..5cc4a596b5 100644 --- a/packages/core/src/sessions/permissionResponse.ts +++ b/packages/core/src/sessions/permissionResponse.ts @@ -1,5 +1,72 @@ import type { PermissionRequest } from "@posthog/shared"; +export type PermissionOption = PermissionRequest["options"][number]; + +export function getPermissionOptionMeta(option: PermissionOption): { + customInput: boolean; + description?: string; +} { + const meta = option._meta as + | { customInput?: boolean; description?: string } + | null + | undefined; + return { + customInput: meta?.customInput === true, + ...(meta?.description ? { description: meta.description } : {}), + }; +} + +export function isPermissionApproval(option: PermissionOption): boolean { + return option.kind === "allow_once" || option.kind === "allow_always"; +} + +export function isPermissionRejection(option: PermissionOption): boolean { + return ( + option.kind === "reject_once" || + option.kind === "reject_always" || + option.optionId.includes("reject") + ); +} + +export function permissionOptionUsesCustomInput( + option: PermissionOption, +): boolean { + return ( + isOtherPermissionOption(option.optionId) || + getPermissionOptionMeta(option).customInput + ); +} + +export function selectPlanPermissionOptions(options: PermissionOption[]): { + approvals: PermissionOption[]; + rejection: PermissionOption | null; +} { + const approvals = options.filter(isPermissionApproval); + const rejections = options.filter(isPermissionRejection); + return { + approvals, + rejection: + rejections.find(permissionOptionUsesCustomInput) ?? rejections[0] ?? null, + }; +} + +export function resolveInitialPlanApprovalOption( + approvals: PermissionOption[], + preferredOptionId?: string | null, +): string | undefined { + const has = (optionId: string): boolean => + approvals.some((option) => option.optionId === optionId); + return ( + (preferredOptionId && has(preferredOptionId) + ? preferredOptionId + : undefined) ?? + (has("auto") ? "auto" : undefined) ?? + approvals.find((option) => option.optionId === "default")?.optionId ?? + approvals.find((option) => option.kind === "allow_once")?.optionId ?? + approvals[0]?.optionId + ); +} + const OTHER_OPTION_ID = "_other"; const OTHER_OPTION_ID_ALT = "other"; diff --git a/packages/ui/src/features/permissions/PlanApprovalSelector.tsx b/packages/ui/src/features/permissions/PlanApprovalSelector.tsx index b22a77951f..145add84aa 100644 --- a/packages/ui/src/features/permissions/PlanApprovalSelector.tsx +++ b/packages/ui/src/features/permissions/PlanApprovalSelector.tsx @@ -1,7 +1,8 @@ -import type { - PermissionOption, - SessionConfigOption, -} from "@agentclientprotocol/sdk"; +import type { SessionConfigOption } from "@agentclientprotocol/sdk"; +import { + resolveInitialPlanApprovalOption, + selectPlanPermissionOptions, +} from "@posthog/core/sessions/permissionResponse"; import type { ExecutionMode } from "@posthog/shared"; import { ModeSelector } from "@posthog/ui/features/message-editor/components/ModeSelector"; import { MODE_LABELS } from "@posthog/ui/features/sessions/modeStyles"; @@ -17,21 +18,6 @@ import { type BasePermissionProps, toSelectorOptions } from "./types"; const TITLE = "Implementation Plan"; const QUESTION = "Approve this plan to proceed?"; -function isApprove(option: PermissionOption): boolean { - return option.kind === "allow_once" || option.kind === "allow_always"; -} - -function isReject(option: PermissionOption): boolean { - return option.kind === "reject_once" || option.kind === "reject_always"; -} - -function hasCustomInput(option: PermissionOption): boolean { - return ( - (option._meta as { customInput?: boolean } | null | undefined) - ?.customInput === true - ); -} - // Don't steal focus from an interactive element in a different grid cell // (multi-task view). Mirrors the guard in useActionSelectorState. function isInteractiveElementInDifferentCell( @@ -66,11 +52,8 @@ export function PlanApprovalSelector({ onSelect, onCancel, }: BasePermissionProps) { - const approveOptions = useMemo(() => options.filter(isApprove), [options]); - const rejectOption = useMemo( - () => - options.find((o) => isReject(o) && hasCustomInput(o)) ?? - options.find(isReject), + const { approvals: approveOptions, rejection: rejectOption } = useMemo( + () => selectPlanPermissionOptions(options), [options], ); @@ -87,16 +70,7 @@ export function PlanApprovalSelector({ // via `useMemo` (rather than seeding a `useState` once) means it stays // correct once the store finishes hydrating. const initialMode = useMemo(() => { - const has = (id: string) => approveOptions.some((o) => o.optionId === id); - return ( - (lastApprovalMode && has(lastApprovalMode) - ? lastApprovalMode - : undefined) ?? - (has("auto") ? "auto" : undefined) ?? - approveOptions.find((o) => o.optionId === "default")?.optionId ?? - approveOptions.find((o) => o.kind === "allow_once")?.optionId ?? - approveOptions[0]?.optionId - ); + return resolveInitialPlanApprovalOption(approveOptions, lastApprovalMode); }, [approveOptions, lastApprovalMode]); // Only the user's own pick lives in state; everything else derives from diff --git a/packages/ui/src/features/permissions/types.ts b/packages/ui/src/features/permissions/types.ts index 1505da5062..1794965cda 100644 --- a/packages/ui/src/features/permissions/types.ts +++ b/packages/ui/src/features/permissions/types.ts @@ -3,6 +3,7 @@ import type { RequestPermissionRequest, ToolCallContent, } from "@agentclientprotocol/sdk"; +import { getPermissionOptionMeta } from "@posthog/core/sessions/permissionResponse"; import type { CodeToolKind } from "@posthog/ui/features/sessions/types"; import type { SelectorOption } from "@posthog/ui/primitives/ActionSelector"; @@ -26,14 +27,12 @@ export function toSelectorOptions( options: PermissionOption[], ): SelectorOption[] { return options.map((opt) => { - const meta = opt._meta as - | { description?: string; customInput?: boolean } - | undefined; + const meta = getPermissionOptionMeta(opt); return { id: opt.optionId, label: opt.name, - description: meta?.description, - customInput: meta?.customInput, + description: meta.description, + customInput: meta.customInput, }; }); } From 046422e63a97b1c2125b878cc17b7aa734cb3edd Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:22:47 +0300 Subject: [PATCH 15/42] refactor(core): extract composer controls Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../src/task-detail/composerControls.test.ts | 27 +++++ .../core/src/task-detail/composerControls.ts | 99 +++++++++++++++++++ .../components/UnifiedModelSelector.tsx | 4 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/task-detail/composerControls.test.ts create mode 100644 packages/core/src/task-detail/composerControls.ts diff --git a/packages/core/src/task-detail/composerControls.test.ts b/packages/core/src/task-detail/composerControls.test.ts new file mode 100644 index 0000000000..0ba02d02cc --- /dev/null +++ b/packages/core/src/task-detail/composerControls.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; +import { resolveComposerPrimaryAction } from "./composerControls"; + +describe("resolveComposerPrimaryAction", () => { + it.each([ + [{ hasContent: true }, "send"], + [{ canStop: true }, "stop"], + [{ canStop: true, hasContent: true }, "send"], + [{ canStop: true, hasContent: true, allowSendWhileRunning: false }, "stop"], + [{ isRecording: true }, "mic-stop"], + [{}, "mic"], + [{ disabled: true, hasContent: true }, "disabled"], + [{ isTranscribing: true }, "disabled"], + ])("derives %s", (overrides, expected) => { + expect( + resolveComposerPrimaryAction({ + hasContent: false, + disabled: false, + isRecording: false, + isTranscribing: false, + canStop: false, + allowSendWhileRunning: true, + ...overrides, + }), + ).toBe(expected); + }); +}); diff --git a/packages/core/src/task-detail/composerControls.ts b/packages/core/src/task-detail/composerControls.ts new file mode 100644 index 0000000000..e9694cccef --- /dev/null +++ b/packages/core/src/task-detail/composerControls.ts @@ -0,0 +1,99 @@ +import { + type Adapter, + type CloudTaskConfigOption, + DEFAULT_REASONING_EFFORT, + isRestrictedModelOption, + isSupportedReasoningEffort, + type SupportedReasoningEffort, +} from "@posthog/shared"; + +export interface ComposerModelOption { + value: string; + label: string; + description?: string; + disabled: boolean; +} + +export function getModelConfigOption( + configOptions: readonly CloudTaskConfigOption[], +): CloudTaskConfigOption { + const option = configOptions.find((item) => item.category === "model"); + if (!option) throw new Error("Cloud task model configuration is unavailable"); + return option; +} + +export function getComposerModelOptions( + modelOption: CloudTaskConfigOption, +): ComposerModelOption[] { + return modelOption.options.map((option) => ({ + value: option.value, + label: option.name, + description: option.description, + disabled: isRestrictedModelOption(option._meta), + })); +} + +export function getConfigOptionLabel( + options: ReadonlyArray<{ value: string; name: string }>, + value: string | undefined, +): string | undefined { + return options.find((option) => option.value === value)?.name ?? value; +} + +export function resolveAvailableModel( + modelOption: CloudTaskConfigOption, + value: string, +): string { + const selected = modelOption.options.find((option) => option.value === value); + return selected && !isRestrictedModelOption(selected._meta) + ? value + : modelOption.currentValue; +} + +export function resolveComposerModelChange({ + adapter, + modelOption, + requestedModel, + reasoning, +}: { + adapter: Adapter; + modelOption: CloudTaskConfigOption; + requestedModel: string; + reasoning: SupportedReasoningEffort; +}): { model: string; reasoning: SupportedReasoningEffort } { + const model = resolveAvailableModel(modelOption, requestedModel); + return { + model, + reasoning: isSupportedReasoningEffort(adapter, model, reasoning) + ? reasoning + : DEFAULT_REASONING_EFFORT, + }; +} + +export type ComposerPrimaryAction = + | "send" + | "stop" + | "mic" + | "mic-stop" + | "disabled"; + +export function resolveComposerPrimaryAction({ + hasContent, + disabled, + isRecording, + isTranscribing, + canStop, + allowSendWhileRunning, +}: { + hasContent: boolean; + disabled: boolean; + isRecording: boolean; + isTranscribing: boolean; + canStop: boolean; + allowSendWhileRunning: boolean; +}): ComposerPrimaryAction { + if (disabled || isTranscribing) return "disabled"; + if (canStop && (!allowSendWhileRunning || !hasContent)) return "stop"; + if (hasContent && !isRecording) return "send"; + return isRecording ? "mic-stop" : "mic"; +} diff --git a/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx b/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx index 06b3956f41..d34dbd9135 100644 --- a/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx +++ b/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx @@ -9,6 +9,7 @@ import { Robot, Spinner, } from "@phosphor-icons/react"; +import { getConfigOptionLabel } from "@posthog/core/task-detail/composerControls"; import { Button, DropdownMenu, @@ -78,8 +79,7 @@ export function UnifiedModelSelector({ }, [selectOption]); const currentValue = selectOption?.currentValue; - const currentLabel = - options.find((opt) => opt.value === currentValue)?.name ?? currentValue; + const currentLabel = getConfigOptionLabel(options, currentValue); const otherAdapter = getOtherAdapter(adapter); From 37f1c0ba9d4125f2ff41da71e43e60a2c8c71bf3 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:23:30 +0300 Subject: [PATCH 16/42] refactor(core): extract composer model policy Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../task-detail/composerModelPolicy.test.ts | 42 +++++++++++++++++++ .../src/task-detail/composerModelPolicy.ts | 35 ++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 packages/core/src/task-detail/composerModelPolicy.test.ts create mode 100644 packages/core/src/task-detail/composerModelPolicy.ts diff --git a/packages/core/src/task-detail/composerModelPolicy.test.ts b/packages/core/src/task-detail/composerModelPolicy.test.ts new file mode 100644 index 0000000000..235c6199f6 --- /dev/null +++ b/packages/core/src/task-detail/composerModelPolicy.test.ts @@ -0,0 +1,42 @@ +import { + type Adapter, + type CloudTaskConfigOption, + DEFAULT_GATEWAY_MODEL, + restrictedModelMeta, + type SupportedReasoningEffort, +} from "@posthog/shared"; +import { expect, it } from "vitest"; +import { resolveCloudComposerModelChange } from "./composerModelPolicy"; + +const modelOption: CloudTaskConfigOption = { + id: "model", + name: "Model", + type: "select", + currentValue: DEFAULT_GATEWAY_MODEL, + options: [ + { value: DEFAULT_GATEWAY_MODEL, name: "Claude" }, + { value: "restricted", name: "Restricted", _meta: restrictedModelMeta() }, + { value: "gpt-5.3-codex", name: "Codex" }, + ], + category: "model", + description: "Choose a model", +}; + +it.each([ + ["claude", DEFAULT_GATEWAY_MODEL, "high", DEFAULT_GATEWAY_MODEL, "high"], + ["claude", "restricted", "high", DEFAULT_GATEWAY_MODEL, "high"], + ["claude", "missing", "high", DEFAULT_GATEWAY_MODEL, "high"], + ["codex", "gpt-5.3-codex", "xhigh", "gpt-5.3-codex", "high"], +] as const)( + "resolves %s model %s with %s reasoning", + (adapter, requestedModel, reasoning, expectedModel, expectedReasoning) => { + expect( + resolveCloudComposerModelChange({ + adapter: adapter as Adapter, + modelOption, + requestedModel, + reasoning: reasoning as SupportedReasoningEffort, + }), + ).toEqual({ model: expectedModel, reasoning: expectedReasoning }); + }, +); diff --git a/packages/core/src/task-detail/composerModelPolicy.ts b/packages/core/src/task-detail/composerModelPolicy.ts new file mode 100644 index 0000000000..d9783335f2 --- /dev/null +++ b/packages/core/src/task-detail/composerModelPolicy.ts @@ -0,0 +1,35 @@ +import { + type Adapter, + type CloudTaskConfigOption, + DEFAULT_REASONING_EFFORT, + isRestrictedModelOption, + isSupportedReasoningEffort, + type SupportedReasoningEffort, +} from "@posthog/shared"; + +export function resolveCloudComposerModelChange({ + adapter, + modelOption, + requestedModel, + reasoning, +}: { + adapter: Adapter; + modelOption: CloudTaskConfigOption; + requestedModel: string; + reasoning: SupportedReasoningEffort; +}): { model: string; reasoning: SupportedReasoningEffort } { + const selected = modelOption.options.find( + (option) => option.value === requestedModel, + ); + const model = + selected && !isRestrictedModelOption(selected._meta) + ? requestedModel + : modelOption.currentValue; + + return { + model, + reasoning: isSupportedReasoningEffort(adapter, model, reasoning) + ? reasoning + : DEFAULT_REASONING_EFFORT, + }; +} From 0a1c884c84bc23155b1d1e0420da7c00828bad89 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 02:14:00 +0300 Subject: [PATCH 17/42] fix(core): prefer streamed plan content Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/core/src/sessions/planApprovalPresentation.test.ts | 4 ++-- packages/core/src/sessions/planApprovalPresentation.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/sessions/planApprovalPresentation.test.ts b/packages/core/src/sessions/planApprovalPresentation.test.ts index 27f9636189..8519c8a41f 100644 --- a/packages/core/src/sessions/planApprovalPresentation.test.ts +++ b/packages/core/src/sessions/planApprovalPresentation.test.ts @@ -18,12 +18,12 @@ describe("extractPlanText", () => { expect(extractPlanText(toolCall)).toBe(expected); }); - it("prefers the canonical raw plan over rendered content", () => { + it("prefers streamed content over stale raw input", () => { expect( extractPlanText({ rawInput: { plan: "Canonical" }, content: [{ text: "Rendered" }], }), - ).toBe("Canonical"); + ).toBe("Rendered"); }); }); diff --git a/packages/core/src/sessions/planApprovalPresentation.ts b/packages/core/src/sessions/planApprovalPresentation.ts index 2dd43553b6..0817576da7 100644 --- a/packages/core/src/sessions/planApprovalPresentation.ts +++ b/packages/core/src/sessions/planApprovalPresentation.ts @@ -12,12 +12,12 @@ export function extractPlanText(toolCall: { rawInput?: { plan?: unknown } | null; content?: readonly unknown[] | null; }): string | null { - const rawPlan = toolCall.rawInput?.plan; - if (typeof rawPlan === "string" && rawPlan.trim()) return rawPlan; - for (const item of toolCall.content ?? []) { const text = extractTextContent(item); if (text?.trim()) return text; } + + const rawPlan = toolCall.rawInput?.plan; + if (typeof rawPlan === "string" && rawPlan.trim()) return rawPlan; return null; } From e27b337284941537e9cb5e6ff383aab58a7166b0 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:24:43 +0300 Subject: [PATCH 18/42] refactor(core): extract session presentation semantics Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../core/src/sessions/posthogExecDisplay.ts | 85 +++++++++++ .../src/sessions/thinkingActivities.test.ts | 17 +++ .../core/src/sessions/thinkingActivities.ts | 103 +++++++++++++ .../mcp-apps/components/McpToolView.tsx | 2 +- .../features/permissions/McpPermission.tsx | 4 +- .../utils/posthog-exec-display.test.ts | 4 +- .../posthog-mcp/utils/posthog-exec-display.ts | 136 ------------------ .../components/GeneratingIndicator.tsx | 102 +------------ 8 files changed, 216 insertions(+), 237 deletions(-) create mode 100644 packages/core/src/sessions/posthogExecDisplay.ts create mode 100644 packages/core/src/sessions/thinkingActivities.test.ts create mode 100644 packages/core/src/sessions/thinkingActivities.ts delete mode 100644 packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.ts diff --git a/packages/core/src/sessions/posthogExecDisplay.ts b/packages/core/src/sessions/posthogExecDisplay.ts new file mode 100644 index 0000000000..5b3029a535 --- /dev/null +++ b/packages/core/src/sessions/posthogExecDisplay.ts @@ -0,0 +1,85 @@ +import { parseMcpToolName } from "@posthog/shared"; + +const POSTHOG_SERVER_RE = /^(?:plugin_)?posthog(?:_[^_]+)*$/; +const POSTHOG_VERB_RE = + /^\s*(tools|search|info|schema|call)(?:\s+([\s\S]*))?\s*$/; +const POSTHOG_CALL_BODY_RE = /^(?:--json\s+)?([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; +const POSTHOG_TOOL_NAME_RE = /^([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; + +export interface PostHogExecDisplay { + label: string; + input?: string; +} + +export function isPostHogExecTool(toolName: string): boolean { + const mcp = parseMcpToolName(toolName); + return !!mcp && mcp.tool === "exec" && POSTHOG_SERVER_RE.test(mcp.server); +} + +export function getPostHogExecDisplay( + toolInput: unknown, +): PostHogExecDisplay | null { + if (!toolInput || typeof toolInput !== "object") return null; + const input = toolInput as { command?: unknown; input?: unknown }; + if (typeof input.command !== "string") return null; + const match = input.command.match(POSTHOG_VERB_RE); + if (!match) return null; + const verb = match[1] as "tools" | "search" | "info" | "schema" | "call"; + const rest = (match[2] ?? "").trim(); + const explicitInput = readExplicitInput(input.input); + + switch (verb) { + case "tools": + return { label: "List tools", input: undefined }; + case "search": + return { + label: "Search tools", + input: explicitInput ?? (rest || undefined), + }; + case "info": + return { label: rest ? `Read ${rest}` : "Read tool", input: undefined }; + case "schema": { + const schema = rest.match(POSTHOG_TOOL_NAME_RE); + if (!schema) return { label: "Inspect schema", input: undefined }; + const path = explicitInput ?? ((schema[2] ?? "").trim() || undefined); + return { + label: path + ? `Inspect ${schema[1]}.${path}` + : `Inspect ${schema[1]} fields`, + input: undefined, + }; + } + case "call": { + const call = rest.match(POSTHOG_CALL_BODY_RE); + if (!call) return null; + return { + label: call[1], + input: explicitInput ?? ((call[2] ?? "").trim() || undefined), + }; + } + } +} + +function readExplicitInput(value: unknown): string | undefined { + if (value === undefined || value === null) return undefined; + if (typeof value === "string") return value.trim() || undefined; + try { + return JSON.stringify(value); + } catch { + return undefined; + } +} + +export function formatPosthogExecBody( + input: string | undefined, +): string | undefined { + if (!input) return undefined; + try { + const parsed = JSON.parse(input); + if (parsed && typeof parsed === "object") + return JSON.stringify(parsed, null, 2); + } catch { + return input; + } + return input; +} diff --git a/packages/core/src/sessions/thinkingActivities.test.ts b/packages/core/src/sessions/thinkingActivities.test.ts new file mode 100644 index 0000000000..1b1ca94aa6 --- /dev/null +++ b/packages/core/src/sessions/thinkingActivities.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from "vitest"; +import { + pickNextThinkingActivity, + pickThinkingActivity, + THINKING_ACTIVITIES, +} from "./thinkingActivities"; + +describe("thinking activities", () => { + it("selects from a bounded random value", () => { + expect(pickThinkingActivity(0)).toBe("Booping"); + expect(pickThinkingActivity(1)).toBe(THINKING_ACTIVITIES.at(-1)); + }); + + it("always advances when the random pick matches the current activity", () => { + expect(pickNextThinkingActivity("Booping", 0)).toBe("Crunching"); + }); +}); diff --git a/packages/core/src/sessions/thinkingActivities.ts b/packages/core/src/sessions/thinkingActivities.ts new file mode 100644 index 0000000000..0046775ffc --- /dev/null +++ b/packages/core/src/sessions/thinkingActivities.ts @@ -0,0 +1,103 @@ +export const THINKING_ACTIVITIES = [ + "Booping", + "Crunching", + "Digging", + "Fetching", + "Inferring", + "Indexing", + "Juggling", + "Noodling", + "Peeking", + "Percolating", + "Poking", + "Pondering", + "Scanning", + "Scrambling", + "Sifting", + "Sniffing", + "Spelunking", + "Tinkering", + "Unraveling", + "Decoding", + "Trekking", + "Sorting", + "Trimming", + "Mulling", + "Surfacing", + "Rummaging", + "Scouting", + "Scouring", + "Threading", + "Hunting", + "Swizzling", + "Grokking", + "Hedging", + "Scheming", + "Unfurling", + "Puzzling", + "Dissecting", + "Stacking", + "Snuffling", + "Hashing", + "Clustering", + "Teasing", + "Cranking", + "Merging", + "Snooping", + "Rewiring", + "Bundling", + "Linking", + "Mapping", + "Tickling", + "Flicking", + "Hopping", + "Rolling", + "Zipping", + "Twisting", + "Blooming", + "Sparking", + "Nesting", + "Looping", + "Wiring", + "Snipping", + "Zoning", + "Tracing", + "Warping", + "Twinkling", + "Flipping", + "Priming", + "Snagging", + "Scuttling", + "Framing", + "Sharpening", + "Flibbertigibbeting", + "Kerfuffling", + "Dithering", + "Discombobulating", + "Rambling", + "Befuddling", + "Waffling", + "Muckling", + "Hobnobbing", + "Galumphing", + "Puttering", + "Whiffling", + "Thinking", +] as const; + +export function pickThinkingActivity(randomValue: number): string { + const bounded = Math.max(0, Math.min(randomValue, 0.999999999999)); + return THINKING_ACTIVITIES[Math.floor(bounded * THINKING_ACTIVITIES.length)]; +} + +export function pickNextThinkingActivity( + current: string, + randomValue: number, +): string { + const picked = pickThinkingActivity(randomValue); + if (picked !== current || THINKING_ACTIVITIES.length <= 1) return picked; + const index = THINKING_ACTIVITIES.indexOf( + current as (typeof THINKING_ACTIVITIES)[number], + ); + return THINKING_ACTIVITIES[(index + 1) % THINKING_ACTIVITIES.length]; +} diff --git a/packages/ui/src/features/mcp-apps/components/McpToolView.tsx b/packages/ui/src/features/mcp-apps/components/McpToolView.tsx index 2a40dc99bc..6e99d80395 100644 --- a/packages/ui/src/features/mcp-apps/components/McpToolView.tsx +++ b/packages/ui/src/features/mcp-apps/components/McpToolView.tsx @@ -2,7 +2,7 @@ import { Plugs } from "@phosphor-icons/react"; import { getPostHogExecDisplay, isPostHogExecTool, -} from "../../posthog-mcp/utils/posthog-exec-display"; +} from "@posthog/core/sessions/posthogExecDisplay"; import { useChatThreadChrome } from "../../sessions/components/chat-thread/chatThreadChrome"; import { ToolRow } from "../../sessions/components/session-update/ToolRow"; import { diff --git a/packages/ui/src/features/permissions/McpPermission.tsx b/packages/ui/src/features/permissions/McpPermission.tsx index 6cf59f90cf..cd5ee5961d 100644 --- a/packages/ui/src/features/permissions/McpPermission.tsx +++ b/packages/ui/src/features/permissions/McpPermission.tsx @@ -1,9 +1,9 @@ -import { mcpToolKey, readMcpToolDescriptor } from "@posthog/shared"; import { formatPosthogExecBody, getPostHogExecDisplay, isPostHogExecTool, -} from "@posthog/ui/features/posthog-mcp/utils/posthog-exec-display"; +} from "@posthog/core/sessions/posthogExecDisplay"; +import { mcpToolKey, readMcpToolDescriptor } from "@posthog/shared"; import { formatInput } from "@posthog/ui/features/sessions/components/session-update/toolCallUtils"; import { ActionSelector } from "@posthog/ui/primitives/ActionSelector"; import { Box, Code } from "@radix-ui/themes"; diff --git a/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.test.ts b/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.test.ts index 08aa1b6f74..5a8603b301 100644 --- a/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.test.ts +++ b/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it } from "vitest"; import { formatPosthogExecBody, getPostHogExecDisplay, isPostHogExecTool, -} from "./posthog-exec-display"; +} from "@posthog/core/sessions/posthogExecDisplay"; +import { describe, expect, it } from "vitest"; describe("isPostHogExecTool", () => { it("matches the bare posthog exec tool", () => { diff --git a/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.ts b/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.ts deleted file mode 100644 index 50cda82a30..0000000000 --- a/packages/ui/src/features/posthog-mcp/utils/posthog-exec-display.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * The PostHog MCP exposes a single `exec` dispatcher that runs CLI-style - * subcommands. Generic MCP rendering would show this as - * `posthog - exec (MCP) {"command":"call execute-sql {…}"}` — pure plumbing - * with the dispatched action buried inside a JSON wrapper. - * - * These helpers pull the action out of the `command` string so the row can - * read `posthog - execute-sql {…}` (call), `posthog - Read execute-sql` - * (info), `posthog - Inspect query-trends.series` (schema), - * `posthog - Search tools query-` (search), or `posthog - List tools` - * (tools) instead. - * - * Supported verbs (per the `exec` tool description): - * tools — list every tool - * search — search by name/title/description - * info — show description + input schema - * schema [field_path] — drill into a specific field - * call [--json] — invoke a tool - */ - -import { parseMcpToolName } from "@posthog/shared"; - -// A PostHog MCP server name: optional `plugin_` prefix, `posthog`, then any -// number of `_` parts (e.g. `posthog`, `posthog_cloud`, -// `plugin_posthog_posthog`). The `exec` dispatcher lives on these servers. -const POSTHOG_SERVER_RE = /^(?:plugin_)?posthog(?:_[^_]+)*$/; - -const POSTHOG_VERB_RE = - /^\s*(tools|search|info|schema|call)(?:\s+([\s\S]*))?\s*$/; -const POSTHOG_CALL_BODY_RE = /^(?:--json\s+)?([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; -const POSTHOG_TOOL_NAME_RE = /^([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; - -export interface PostHogExecDisplay { - /** Replaces the tool name in the title — e.g. "execute-sql", "Read execute-sql". */ - label: string; - /** Args to show as the input preview, undefined when there is none to display. */ - input?: string; -} - -export function isPostHogExecTool(toolName: string): boolean { - const mcp = parseMcpToolName(toolName); - return !!mcp && mcp.tool === "exec" && POSTHOG_SERVER_RE.test(mcp.server); -} - -export function getPostHogExecDisplay( - toolInput: unknown, -): PostHogExecDisplay | null { - if (!toolInput || typeof toolInput !== "object") return null; - const obj = toolInput as { command?: unknown; input?: unknown }; - - if (typeof obj.command !== "string") return null; - const verbMatch = obj.command.match(POSTHOG_VERB_RE); - if (!verbMatch) return null; - - const verb = verbMatch[1] as "tools" | "search" | "info" | "schema" | "call"; - const rest = (verbMatch[2] ?? "").trim(); - const explicitInput = readExplicitInput(obj.input); - - switch (verb) { - case "tools": - // `tools` returns names only, not full schemas — "List", not "Read". - return { label: "List tools", input: undefined }; - - case "search": - return { - label: "Search tools", - input: explicitInput ?? (rest.length > 0 ? rest : undefined), - }; - - case "info": - // `info ` — fold the tool name into the label so the args slot stays clean. - return rest.length > 0 - ? { label: `Read ${rest}`, input: undefined } - : { label: "Read tool", input: undefined }; - - case "schema": { - // `schema [field_path]` is the drill-down verb. Fold the - // tool + path into a dotted locator so it reads as one path. - const m = rest.match(POSTHOG_TOOL_NAME_RE); - if (!m) return { label: "Inspect schema", input: undefined }; - const subTool = m[1]; - const fieldPath = (m[2] ?? "").trim(); - const path = - explicitInput ?? (fieldPath.length > 0 ? fieldPath : undefined); - return { - label: path - ? `Inspect ${subTool}.${path}` - : `Inspect ${subTool} fields`, - input: undefined, - }; - } - - case "call": { - // `call [--json] [json_input]` — collapse the verb, surface the - // sub-tool as the label and the JSON body as args. - const m = rest.match(POSTHOG_CALL_BODY_RE); - if (!m) return null; - const subTool = m[1]; - const args = (m[2] ?? "").trim(); - return { - label: subTool, - input: explicitInput ?? (args.length > 0 ? args : undefined), - }; - } - } -} - -function readExplicitInput(value: unknown): string | undefined { - if (value === undefined || value === null) return undefined; - if (typeof value === "string") return value.trim() || undefined; - try { - return JSON.stringify(value); - } catch { - return undefined; - } -} - -/** - * Pretty-prints the unwrapped exec args for display in the permission dialog - * body — JSON payloads (the `call` case) render multi-line; non-JSON args - * (e.g. a `search` regex) pass through unchanged. - */ -export function formatPosthogExecBody( - input: string | undefined, -): string | undefined { - if (!input) return undefined; - try { - const parsed = JSON.parse(input); - if (parsed && typeof parsed === "object") { - return JSON.stringify(parsed, null, 2); - } - } catch { - // not JSON — fall through and show raw - } - return input; -} diff --git a/packages/ui/src/features/sessions/components/GeneratingIndicator.tsx b/packages/ui/src/features/sessions/components/GeneratingIndicator.tsx index ba2a202e21..47343950d1 100644 --- a/packages/ui/src/features/sessions/components/GeneratingIndicator.tsx +++ b/packages/ui/src/features/sessions/components/GeneratingIndicator.tsx @@ -1,109 +1,19 @@ import { Brain, Circle } from "@phosphor-icons/react"; +import { + pickNextThinkingActivity, + pickThinkingActivity, +} from "@posthog/core/sessions/thinkingActivities"; import { Flex, Text } from "@radix-ui/themes"; import { useEffect, useRef, useState } from "react"; -const THINKING_MESSAGES = [ - "Booping", - "Crunching", - "Digging", - "Fetching", - "Inferring", - "Indexing", - "Juggling", - "Noodling", - "Peeking", - "Percolating", - "Poking", - "Pondering", - "Scanning", - "Scrambling", - "Sifting", - "Sniffing", - "Spelunking", - "Tinkering", - "Unraveling", - "Decoding", - "Trekking", - "Sorting", - "Trimming", - "Mulling", - "Surfacing", - "Rummaging", - "Scouting", - "Scouring", - "Threading", - "Hunting", - "Swizzling", - "Grokking", - "Hedging", - "Scheming", - "Unfurling", - "Puzzling", - "Dissecting", - "Stacking", - "Snuffling", - "Hashing", - "Clustering", - "Teasing", - "Cranking", - "Merging", - "Snooping", - "Rewiring", - "Bundling", - "Linking", - "Mapping", - "Tickling", - "Flicking", - "Hopping", - "Rolling", - "Zipping", - "Twisting", - "Blooming", - "Sparking", - "Nesting", - "Wiring", - "Snipping", - "Zoning", - "Tracing", - "Warping", - "Twinkling", - "Flipping", - "Priming", - "Snagging", - "Scuttling", - "Framing", - "Sharpening", - "Flibbertigibbeting", - "Kerfuffling", - "Dithering", - "Discombobulating", - "Rambling", - "Befuddling", - "Waffling", - "Muckling", - "Hobnobbing", - "Galumphing", - "Puttering", - "Whiffling", - "Thinking", -]; - function getRandomThinkingMessage(): string { - return THINKING_MESSAGES[ - Math.floor(Math.random() * THINKING_MESSAGES.length) - ]; + return pickThinkingActivity(Math.random()); } /** Pick a new word that differs from the current one, so consecutive changes * always read as a change. */ function getNextThinkingMessage(current: string): string { - if (THINKING_MESSAGES.length <= 1) return THINKING_MESSAGES[0]; - let next = current; - while (next === current) { - next = - THINKING_MESSAGES[Math.floor(Math.random() * THINKING_MESSAGES.length)]; - } - return next; + return pickNextThinkingActivity(current, Math.random()); } export function formatDuration(ms: number, fractionDigits = 2): string { From 04a7f525faf4ce436eb6d14905758c091f64511c Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:25:57 +0300 Subject: [PATCH 19/42] refactor(api-client): expose shared automation contracts Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../api-client/src/posthog-client.test.ts | 27 ++++++ packages/api-client/src/posthog-client.ts | 93 +++++++++++++------ 2 files changed, 90 insertions(+), 30 deletions(-) diff --git a/packages/api-client/src/posthog-client.test.ts b/packages/api-client/src/posthog-client.test.ts index da93bc7960..cc25844ea1 100644 --- a/packages/api-client/src/posthog-client.test.ts +++ b/packages/api-client/src/posthog-client.test.ts @@ -475,6 +475,33 @@ describe("PostHogAPIClient", () => { ); }); + it.each([true, false])("forwards auto publish %s", async (autoPublish) => { + const client = new PostHogAPIClient( + "http://localhost:8000", + async () => "token", + async () => "token", + 123, + ); + const post = vi.fn().mockResolvedValue({ + id: "task-123", + title: "Task", + description: "Task", + created_at: "2026-04-14T00:00:00Z", + updated_at: "2026-04-14T00:00:00Z", + origin_product: "user_created", + }); + (client as unknown as { api: { post: typeof post } }).api = { post }; + + await client.runTaskInCloud("task-123", null, { autoPublish }); + + expect(post).toHaveBeenCalledWith( + "/api/projects/{project_id}/tasks/{id}/run/", + expect.objectContaining({ + body: expect.objectContaining({ auto_publish: autoPublish }), + }), + ); + }); + it("rejects unsupported reasoning effort for cloud Codex runs", async () => { const client = new PostHogAPIClient( "http://localhost:8000", diff --git a/packages/api-client/src/posthog-client.ts b/packages/api-client/src/posthog-client.ts index b2ef9fa8a8..75bf5183ea 100644 --- a/packages/api-client/src/posthog-client.ts +++ b/packages/api-client/src/posthog-client.ts @@ -4,30 +4,22 @@ import type { CloudMcpServerImport, CloudMcpServerRelayDesignation, CloudRunSource, - CreateTaskAutomationOptions, ExecutionMode, PrAuthorshipMode, SourceProduct, SourceType, StoredLogEntry, - TaskAutomation, TaskRunArtifactMetadata, - UpdateTaskAutomationOptions, } from "@posthog/shared"; import { buildCloudTaskConfigOptions, type CloudTaskConfigOption, - createTaskAutomationSchema, DISMISSAL_REASON_OPTIONS, type DismissalReasonOptionValue, getCloudTaskGatewayUrl, isSupportedReasoningEffort, normalizeGatewayModelsResponse, resolveCloudInitialPermissionMode, - taskAutomationListSchema, - taskAutomationSchema, - taskAutomationValidationErrorSchema, - updateTaskAutomationSchema, } from "@posthog/shared"; import type { AgentAnalyticsData, @@ -210,11 +202,22 @@ export class TaskAutomationValidationError extends Error { function rethrowTaskAutomationError(error: unknown): never { if (error instanceof ApiRequestError && error.status === 400) { - const validationError = taskAutomationValidationErrorSchema.safeParse( - error.body, - ); - if (validationError.success) { - throw new TaskAutomationValidationError(validationError.data); + const body = error.body; + if ( + typeof body === "object" && + body !== null && + "detail" in body && + typeof body.detail === "string" + ) { + throw new TaskAutomationValidationError({ + detail: body.detail, + code: + "code" in body && typeof body.code === "string" + ? body.code + : "invalid_input", + attr: + "attr" in body && typeof body.attr === "string" ? body.attr : null, + }); } } @@ -250,6 +253,41 @@ export type { export type Evaluation = Schemas.Evaluation; +export type TaskAutomation = Omit< + Schemas.TaskAutomation, + "github_integration" | "timezone" | "template_id" | "enabled" +> & { + github_integration: number | null; + timezone: string | null; + template_id: string | null; + enabled: boolean; +}; + +export type CreateTaskAutomationOptions = Pick< + Schemas.TaskAutomation, + "name" | "prompt" | "repository" | "cron_expression" +> & + Partial< + Pick< + Schemas.TaskAutomation, + "github_integration" | "template_id" | "enabled" + > + > & { timezone: string }; + +export type UpdateTaskAutomationOptions = Partial; + +function normalizeTaskAutomation( + automation: Schemas.TaskAutomation, +): TaskAutomation { + return { + ...automation, + github_integration: automation.github_integration ?? null, + timezone: automation.timezone ?? null, + template_id: automation.template_id ?? null, + enabled: automation.enabled ?? true, + }; +} + export interface UserGitHubIntegration { id: string; kind: "github"; @@ -787,7 +825,7 @@ function buildCloudRunRequestBody( if (options?.prAuthorshipMode) { body.pr_authorship_mode = options.prAuthorshipMode; } - if (options?.autoPublish) { + if (options?.autoPublish !== undefined) { body.auto_publish = options.autoPublish; } if (options?.rtkEnabled === false) { @@ -2486,7 +2524,7 @@ export class PostHogAPIClient { }, ); - return taskAutomationListSchema.parse(data).results; + return data.results.map(normalizeTaskAutomation); } async getTaskAutomation(automationId: string): Promise { @@ -2498,24 +2536,22 @@ export class PostHogAPIClient { }, ); - return taskAutomationSchema.parse(data); + return normalizeTaskAutomation(data); } async createTaskAutomation( options: CreateTaskAutomationOptions, ): Promise { const teamId = await this.getTeamId(); - const body = createTaskAutomationSchema.parse(options); - try { const data = await this.api.post( `/api/projects/{project_id}/task_automations/`, { path: { project_id: teamId.toString() }, - body: body as Schemas.TaskAutomation, + body: options as Schemas.TaskAutomation, }, ); - return taskAutomationSchema.parse(data); + return normalizeTaskAutomation(data); } catch (error) { rethrowTaskAutomationError(error); } @@ -2526,17 +2562,15 @@ export class PostHogAPIClient { updates: UpdateTaskAutomationOptions, ): Promise { const teamId = await this.getTeamId(); - const body = updateTaskAutomationSchema.parse(updates); - try { const data = await this.api.patch( `/api/projects/{project_id}/task_automations/{id}/`, { path: { project_id: teamId.toString(), id: automationId }, - body, + body: updates, }, ); - return taskAutomationSchema.parse(data); + return normalizeTaskAutomation(data); } catch (error) { rethrowTaskAutomationError(error); } @@ -2559,7 +2593,9 @@ export class PostHogAPIClient { path, url: new URL(`${this.api.baseUrl}${path}`), }); - return taskAutomationSchema.parse(await response.json()); + return normalizeTaskAutomation( + (await response.json()) as Schemas.TaskAutomation, + ); } catch (error) { rethrowTaskAutomationError(error); } @@ -2604,16 +2640,13 @@ export class PostHogAPIClient { return normalizeTaskResponse(data, { teamId }); } - async updateTask( - taskId: string, - updates: Partial, - ): Promise { + async updateTask(taskId: string, updates: Partial): Promise { const teamId = await this.getTeamId(); const data = await this.api.patch( `/api/projects/{project_id}/tasks/{id}/`, { path: { project_id: teamId.toString(), id: taskId }, - body: updates, + body: updates as unknown as Partial, }, ); From 0b25fcd86abfe0ce4a8b40f4c7fce246786d6292 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:26:21 +0300 Subject: [PATCH 20/42] refactor(core): extract inbox presentation semantics Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../automationTemplatePresentation.ts | 37 +++++++++++++++++++ packages/core/src/inbox/engagement.ts | 27 ++++++++++---- packages/core/src/inbox/reportMembership.ts | 17 +++++++++ 3 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/automations/automationTemplatePresentation.ts diff --git a/packages/core/src/automations/automationTemplatePresentation.ts b/packages/core/src/automations/automationTemplatePresentation.ts new file mode 100644 index 0000000000..7d73244aa7 --- /dev/null +++ b/packages/core/src/automations/automationTemplatePresentation.ts @@ -0,0 +1,37 @@ +import type { TaskAutomation } from "@posthog/api-client/posthog-client"; + +export const SKILL_TEMPLATE_ID_PREFIX = "llm-skill:"; + +export function formatSkillTemplateId(skillName: string): string { + return `${SKILL_TEMPLATE_ID_PREFIX}${skillName.trim()}`; +} + +export function parseSkillTemplateId( + templateId: string | null | undefined, +): string | null { + if (!templateId?.startsWith(SKILL_TEMPLATE_ID_PREFIX)) return null; + const skillName = templateId.slice(SKILL_TEMPLATE_ID_PREFIX.length).trim(); + return skillName || null; +} + +export interface AutomationTemplatePresentation { + templateName: string | null; + repositoryLabel: string | null; + contextLabel: string | null; + secondaryLabel: string; +} + +export function getAutomationTemplatePresentation( + automation: Pick, +): AutomationTemplatePresentation { + const repositoryLabel = automation.repository.trim() || null; + const skillName = parseSkillTemplateId(automation.template_id); + const contextLabel = skillName ? "Skill store" : null; + return { + templateName: + skillName ?? (automation.template_id ? "Template automation" : null), + repositoryLabel, + contextLabel, + secondaryLabel: repositoryLabel ?? contextLabel ?? "No repository context", + }; +} diff --git a/packages/core/src/inbox/engagement.ts b/packages/core/src/inbox/engagement.ts index dc4428ddbb..012ca18dc4 100644 --- a/packages/core/src/inbox/engagement.ts +++ b/packages/core/src/inbox/engagement.ts @@ -174,13 +174,16 @@ export function buildBulkActionEvents( export interface InboxViewedFilterState { sourceProductFilter: string[]; priorityFilter: string[]; - searchQuery: string; + searchQuery?: string; + statusFilter?: readonly string[]; + defaultStatusFilter?: readonly string[]; + suggestedReviewerFilter?: string[]; /** * True when the reviewer scope is the default ("For you"). False when the * user has narrowed to a teammate or the whole project — treated as an * active filter for `has_active_filters`. */ - isDefaultScope: boolean; + isDefaultScope?: boolean; } export interface BuildInboxViewedInput { @@ -192,7 +195,7 @@ export interface BuildInboxViewedInput { /** Server-reported total of reports matching the active query — the headline inbox number. */ totalCount: number; /** Tab badge counts shown in the v2 header (the numbers the user actually sees). */ - tabCounts: { pulls: number; reports: number }; + tabCounts?: { pulls: number; reports: number }; filters: InboxViewedFilterState; } @@ -207,7 +210,8 @@ export interface BuildInboxViewedInput { export function buildInboxViewedProperties( input: BuildInboxViewedInput, ): InboxViewedProperties { - const { visibleReports, totalCount, tabCounts, filters } = input; + const { visibleReports, totalCount, filters } = input; + const tabCounts = input.tabCounts ?? { pulls: 0, reports: totalCount }; const priorityCounts = { P0: 0, P1: 0, P2: 0, P3: 0, P4: 0, unknown: 0 }; const actionabilityCounts = { @@ -237,11 +241,20 @@ export function buildInboxViewedProperties( } } + const statusFiltered = + filters.statusFilter !== undefined && + filters.defaultStatusFilter !== undefined && + (filters.statusFilter.length !== filters.defaultStatusFilter.length || + filters.statusFilter.some( + (status) => !filters.defaultStatusFilter?.includes(status), + )); const hasActiveFilters = filters.sourceProductFilter.length > 0 || filters.priorityFilter.length > 0 || - filters.searchQuery.trim().length > 0 || - !filters.isDefaultScope; + (filters.searchQuery?.trim().length ?? 0) > 0 || + statusFiltered || + (filters.suggestedReviewerFilter?.length ?? 0) > 0 || + filters.isDefaultScope === false; return { report_count: visibleReports.length, @@ -249,7 +262,7 @@ export function buildInboxViewedProperties( ready_count: readyCount, has_active_filters: hasActiveFilters, source_product_filter: filters.sourceProductFilter, - status_filter_count: 0, + status_filter_count: filters.statusFilter?.length ?? 0, is_empty: totalCount === 0, priority_p0_count: priorityCounts.P0, priority_p1_count: priorityCounts.P1, diff --git a/packages/core/src/inbox/reportMembership.ts b/packages/core/src/inbox/reportMembership.ts index edfca5f9e8..e31621fba3 100644 --- a/packages/core/src/inbox/reportMembership.ts +++ b/packages/core/src/inbox/reportMembership.ts @@ -35,6 +35,23 @@ export function isDismissedReport(report: SignalReport): boolean { return report.status === "suppressed" || report.status === "resolved"; } +export function isRestorableReport( + report: Pick, +): boolean { + return report.status === "suppressed"; +} + +export function getImmediatelyActionableReports( + reports: SignalReport[], +): SignalReport[] { + return reports.filter( + (report) => + report.status === "ready" && + report.actionability === "immediately_actionable" && + !report.already_addressed, + ); +} + export type InboxScope = "for-you" | "entire-project" | `teammate:${string}`; export const INBOX_SCOPE_FOR_YOU: InboxScope = "for-you"; From afa5cb66f1dc49d187bb03c698076d03d639ed48 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:26:59 +0300 Subject: [PATCH 21/42] refactor(core): extract inbox activity presentation Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/core/src/inbox/activityLog.ts | 74 +++++++++++++++++++ .../components/detail/ArtefactLogList.tsx | 11 +-- .../components/detail/ArtefactTaskRun.tsx | 25 ++----- 3 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 packages/core/src/inbox/activityLog.ts diff --git a/packages/core/src/inbox/activityLog.ts b/packages/core/src/inbox/activityLog.ts new file mode 100644 index 0000000000..11fb75cab2 --- /dev/null +++ b/packages/core/src/inbox/activityLog.ts @@ -0,0 +1,74 @@ +import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; + +export type ActivityArtefact = Extract< + AnySignalReportArtefact, + { type: "commit" | "task_run" } +>; + +export function selectActivityArtefacts( + artefacts: AnySignalReportArtefact[], +): ActivityArtefact[] { + return artefacts + .filter( + (artefact): artefact is ActivityArtefact => + artefact.type === "commit" || artefact.type === "task_run", + ) + .sort((left, right) => left.created_at.localeCompare(right.created_at)); +} + +export function shortSha(sha: string): string { + return sha.slice(0, 12); +} + +const SIGNALS_TYPE_LABELS: Record = { + research: "Research", + implementation: "Implementation", + repo_selection: "Repo selection", +}; + +export function humanizeIdentifier(value: string): string { + const spaced = value.replace(/[_-]+/g, " ").trim(); + return spaced.charAt(0).toUpperCase() + spaced.slice(1); +} + +export function taskRunLabel(content: { + product: string; + type: string; +}): string { + return content.product === "signals" + ? (SIGNALS_TYPE_LABELS[content.type] ?? humanizeIdentifier(content.type)) + : humanizeIdentifier(content.type); +} + +export function attributionLabel(artefact: { + created_by?: { first_name?: string; email: string } | null; + task_id?: string | null; +}): string | null { + if (artefact.created_by) { + return artefact.created_by.first_name?.trim() || artefact.created_by.email; + } + return artefact.task_id ? "agent" : null; +} + +export type DiffLineKind = "add" | "del" | "hunk" | "context"; + +export interface DiffLine { + text: string; + kind: DiffLineKind; +} + +export function parseDiffLines(diff: string): DiffLine[] { + return diff + .replace(/\n$/, "") + .split("\n") + .map((text) => { + if (text.startsWith("+") && !text.startsWith("+++")) { + return { text, kind: "add" as const }; + } + if (text.startsWith("-") && !text.startsWith("---")) { + return { text, kind: "del" as const }; + } + if (text.startsWith("@@")) return { text, kind: "hunk" as const }; + return { text, kind: "context" as const }; + }); +} diff --git a/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx b/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx index 9170e124b4..58fed94c23 100644 --- a/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx +++ b/packages/ui/src/features/inbox/components/detail/ArtefactLogList.tsx @@ -3,6 +3,7 @@ import { CaretDownIcon, CaretRightIcon, } from "@phosphor-icons/react"; +import { attributionLabel } from "@posthog/core/inbox/activityLog"; import type { ActionabilityJudgmentContent, AnySignalReportArtefact, @@ -70,16 +71,6 @@ function languageFromPath(filePath: string): string { * Who produced the artefact: a user's name, "agent" for task-attributed writes, * or null for system (pipeline) writes and pre-attribution rows. */ -function attributionLabel(artefact: AnySignalReportArtefact): string | null { - if (artefact.created_by) { - return artefact.created_by.first_name?.trim() || artefact.created_by.email; - } - if (artefact.task_id) { - return "agent"; - } - return null; -} - // The generic `SignalReportArtefact` fallback carries `type: string`, so it stays // in every narrowed branch and breaks discriminated-union narrowing — the runtime // `type` dispatch is authoritative (content is set alongside type in the diff --git a/packages/ui/src/features/inbox/components/detail/ArtefactTaskRun.tsx b/packages/ui/src/features/inbox/components/detail/ArtefactTaskRun.tsx index ed07a0d98a..d3fe02b8ed 100644 --- a/packages/ui/src/features/inbox/components/detail/ArtefactTaskRun.tsx +++ b/packages/ui/src/features/inbox/components/detail/ArtefactTaskRun.tsx @@ -1,4 +1,8 @@ import { CaretDownIcon, CaretRightIcon } from "@phosphor-icons/react"; +import { + humanizeIdentifier, + taskRunLabel, +} from "@posthog/core/inbox/activityLog"; import type { Task, TaskRunArtefactContent } from "@posthog/shared/types"; import { TaskLogsPanel } from "@posthog/ui/features/task-detail/components/TaskLogsPanel"; import { taskKeys } from "@posthog/ui/features/tasks/taskKeys"; @@ -6,21 +10,6 @@ import { useAuthenticatedQuery } from "@posthog/ui/hooks/useAuthenticatedQuery"; import { Badge, Box, Text } from "@radix-ui/themes"; import { useState } from "react"; -const SIGNALS_PRODUCT = "signals"; - -// Friendlier labels for the built-in signals-pipeline task types; custom-agent types fall back -// to a humanized form of their identifier. -const SIGNALS_TYPE_LABELS: Record = { - research: "Research", - implementation: "Implementation", - repo_selection: "Repo selection", -}; - -function humanizeIdentifier(value: string): string { - const spaced = value.replace(/[_-]+/g, " ").trim(); - return spaced.charAt(0).toUpperCase() + spaced.slice(1); -} - /** * Renders a `task_run` artefact: loads the referenced task and lets the user * expand it to read the full conversation log (read-only) via `TaskLogsPanel`. @@ -39,10 +28,8 @@ export function ArtefactTaskRun({ ); const task = taskQuery.data; - const isSignals = content.product === SIGNALS_PRODUCT; - const label = isSignals - ? (SIGNALS_TYPE_LABELS[content.type] ?? humanizeIdentifier(content.type)) - : humanizeIdentifier(content.type); + const isSignals = content.product === "signals"; + const label = taskRunLabel(content); const status = task?.latest_run?.status; return ( From a7e4e28fda33b6af9fb35043d512a9b9ed399b47 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:27:37 +0300 Subject: [PATCH 22/42] refactor(core): extract portability contracts Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/api-client/src/types.ts | 30 +++++++++++++++++++ packages/core/src/mcp-servers/presentation.ts | 13 ++++++++ 2 files changed, 43 insertions(+) create mode 100644 packages/core/src/mcp-servers/presentation.ts diff --git a/packages/api-client/src/types.ts b/packages/api-client/src/types.ts index a17f035ad1..0c6146081c 100644 --- a/packages/api-client/src/types.ts +++ b/packages/api-client/src/types.ts @@ -8,3 +8,33 @@ export type McpAuthType = Schemas.MCPAuthTypeEnum; export type McpRecommendedServer = Schemas.MCPServerTemplate; export type McpServerInstallation = Schemas.MCPServerInstallation; export type McpInstallationTool = Schemas.MCPServerInstallationTool; +export type McpOAuthRedirectResponse = Schemas.OAuthRedirectResponse; +export type McpInstallSource = "posthog" | "posthog-code" | "posthog-mobile"; +export type McpInstallResponse = + | McpServerInstallation + | McpOAuthRedirectResponse; + +export interface InstallCustomMcpServerOptions { + name: string; + url: string; + auth_type: McpAuthType; + api_key?: string; + description?: string; + client_id?: string; + client_secret?: string; + install_source?: McpInstallSource; + posthog_code_callback_url?: string; +} + +export interface InstallMcpTemplateOptions { + template_id: string; + api_key?: string; + install_source?: McpInstallSource; + posthog_code_callback_url?: string; +} + +export interface UpdateMcpServerInstallationOptions { + display_name?: string; + description?: string; + is_enabled?: boolean; +} diff --git a/packages/core/src/mcp-servers/presentation.ts b/packages/core/src/mcp-servers/presentation.ts new file mode 100644 index 0000000000..66b3bfd895 --- /dev/null +++ b/packages/core/src/mcp-servers/presentation.ts @@ -0,0 +1,13 @@ +export function isMcpOAuthRedirect( + response: object, +): response is { redirect_url: string } { + return ( + "redirect_url" in response && typeof response.redirect_url === "string" + ); +} + +export function isStdioMcpServer(server: { + transport_type?: string | null; +}): boolean { + return server.transport_type === "stdio"; +} From 1f3ad6a77ca2a4a921c45ed5bbae43d4460dbeac Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:28:04 +0300 Subject: [PATCH 23/42] refactor(ui): reuse inbox identifier formatting Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/ui/src/features/inbox/hooks/useReportTasks.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/ui/src/features/inbox/hooks/useReportTasks.ts b/packages/ui/src/features/inbox/hooks/useReportTasks.ts index 3f2093f148..76ba270a13 100644 --- a/packages/ui/src/features/inbox/hooks/useReportTasks.ts +++ b/packages/ui/src/features/inbox/hooks/useReportTasks.ts @@ -1,3 +1,4 @@ +import { humanizeIdentifier } from "@posthog/core/inbox/activityLog"; import type { SignalReportStatus, Task, @@ -19,11 +20,6 @@ export interface ReportTaskData { startedAt: string; } -function humanizeIdentifier(value: string): string { - const words = value.replace(/[_-]+/g, " ").trim(); - return words.charAt(0).toUpperCase() + words.slice(1); -} - function derivePurpose(taskRun: { product: string; type: string; From 0c3e6fb4d458974e060c02c5fe9640a4901a4bc9 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:29:57 +0300 Subject: [PATCH 24/42] refactor(mobile): adopt shared task runtime Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/package.json | 2 + apps/mobile/src/app/automation/[id].tsx | 2 +- apps/mobile/src/app/automation/create.tsx | 2 +- apps/mobile/src/app/task/[id].tsx | 25 +- apps/mobile/src/app/task/index.tsx | 5 +- .../features/chat/components/ToolMessage.tsx | 7 +- apps/mobile/src/features/inbox/api.ts | 3 +- .../inbox/components/EditReviewersSheet.tsx | 5 +- .../inbox/components/ReviewerFilterSheet.tsx | 2 +- .../inbox/components/ReviewerOptionRow.tsx | 5 +- .../inbox/components/SuggestedReviewers.tsx | 10 +- .../features/inbox/components/TinderView.tsx | 5 +- apps/mobile/src/features/inbox/constants.ts | 14 - .../features/inbox/hooks/useInboxReports.ts | 22 +- .../features/inbox/stores/inboxFilterStore.ts | 8 +- apps/mobile/src/features/inbox/utils.test.ts | 242 +---- apps/mobile/src/features/inbox/utils.ts | 160 --- .../components/SwipeableArchivedDrawerRow.tsx | 2 +- .../features/tasks/api.automations.test.ts | 280 ----- apps/mobile/src/features/tasks/api.test.ts | 78 +- apps/mobile/src/features/tasks/api.ts | 718 +------------ .../src/features/tasks/api.warm.test.ts | 187 ---- .../tasks/components/AutomationDetail.tsx | 5 +- .../tasks/components/AutomationForm.tsx | 20 +- .../tasks/components/AutomationItem.tsx | 5 +- .../tasks/components/AutomationList.tsx | 2 +- .../components/AutomationStatusBadge.tsx | 2 +- .../CreateAutomationScreen.test.tsx | 2 +- .../components/CustomImageBadge.test.tsx | 10 +- .../tasks/components/CustomImageBadge.tsx | 2 +- .../components/GitHubConnectionPrompt.tsx | 5 +- .../tasks/components/ScheduleEditor.tsx | 4 +- .../tasks/components/SwipeableTaskItem.tsx | 8 +- .../tasks/components/TaskItem.test.tsx | 2 +- .../features/tasks/components/TaskItem.tsx | 2 +- .../features/tasks/components/TaskList.tsx | 5 +- .../tasks/components/TaskStatusIcon.test.ts | 12 +- .../tasks/components/TaskStatusIcon.tsx | 2 +- .../tasks/components/taskStatusIconKind.ts | 5 +- .../features/tasks/composer/options.test.ts | 27 + .../src/features/tasks/composer/options.ts | 79 +- .../tasks/hooks/useAutomations.test.ts | 24 +- .../features/tasks/hooks/useAutomations.ts | 31 +- .../tasks/hooks/useCustomImageName.ts | 6 +- .../tasks/hooks/useIntegrations.test.ts | 8 +- .../features/tasks/hooks/useIntegrations.ts | 31 +- .../src/features/tasks/hooks/useTasks.test.ts | 15 +- .../src/features/tasks/hooks/useTasks.ts | 33 +- .../tasks/hooks/useUserIntegrations.ts | 13 +- .../features/tasks/hooks/useWarmTask.test.tsx | 4 +- .../src/features/tasks/hooks/useWarmTask.ts | 34 +- apps/mobile/src/features/tasks/index.ts | 3 - .../tasks/lib/cloudTaskStream.test.ts | 54 + .../src/features/tasks/lib/cloudTaskStream.ts | 962 ++---------------- .../src/features/tasks/lib/sseParser.ts | 89 -- .../tasks/stores/taskSessionStore.test.ts | 22 +- .../features/tasks/stores/taskSessionStore.ts | 26 +- .../features/tasks/stores/taskStore.test.ts | 54 - .../src/features/tasks/stores/taskStore.ts | 48 +- apps/mobile/src/features/tasks/types.ts | 206 +--- .../features/tasks/utils/archiveGuard.test.ts | 54 +- .../src/features/tasks/utils/archiveGuard.ts | 6 - .../tasks/utils/automationSchedule.test.ts | 101 -- .../tasks/utils/automationSchedule.ts | 213 ---- .../tasks/utils/automationStatus.test.ts | 16 +- .../features/tasks/utils/automationStatus.ts | 3 +- .../utils/automationTemplatePresentation.ts | 2 +- .../features/tasks/utils/parseSessionLogs.ts | 93 +- .../tasks/utils/sessionActivity.test.ts | 161 --- .../features/tasks/utils/sessionActivity.ts | 133 --- apps/mobile/src/lib/analytics.ts | 10 +- apps/mobile/src/lib/api.ts | 15 +- apps/mobile/src/lib/posthogApiClient.test.ts | 190 ++++ apps/mobile/src/lib/posthogApiClient.ts | 84 ++ pnpm-lock.yaml | 6 + 75 files changed, 854 insertions(+), 3879 deletions(-) delete mode 100644 apps/mobile/src/features/tasks/api.automations.test.ts delete mode 100644 apps/mobile/src/features/tasks/api.warm.test.ts create mode 100644 apps/mobile/src/features/tasks/composer/options.test.ts create mode 100644 apps/mobile/src/features/tasks/lib/cloudTaskStream.test.ts delete mode 100644 apps/mobile/src/features/tasks/lib/sseParser.ts delete mode 100644 apps/mobile/src/features/tasks/stores/taskStore.test.ts delete mode 100644 apps/mobile/src/features/tasks/utils/automationSchedule.test.ts delete mode 100644 apps/mobile/src/features/tasks/utils/automationSchedule.ts delete mode 100644 apps/mobile/src/features/tasks/utils/sessionActivity.test.ts delete mode 100644 apps/mobile/src/features/tasks/utils/sessionActivity.ts create mode 100644 apps/mobile/src/lib/posthogApiClient.test.ts create mode 100644 apps/mobile/src/lib/posthogApiClient.ts diff --git a/apps/mobile/package.json b/apps/mobile/package.json index f1057c0146..09ebacf3d0 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -27,6 +27,8 @@ "@expo/ui": "0.2.0-beta.9", "@modelcontextprotocol/ext-apps": "^1.2.2", "@modelcontextprotocol/sdk": "^1.29.0", + "@posthog/api-client": "workspace:*", + "@posthog/core": "workspace:*", "@posthog/shared": "workspace:*", "@react-native-async-storage/async-storage": "^2.2.0", "@react-native-community/netinfo": "^12.0.1", diff --git a/apps/mobile/src/app/automation/[id].tsx b/apps/mobile/src/app/automation/[id].tsx index 5983ecdf28..55f9bd5c6d 100644 --- a/apps/mobile/src/app/automation/[id].tsx +++ b/apps/mobile/src/app/automation/[id].tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useState } from "react"; import { @@ -10,7 +11,6 @@ import { ScrollView, View, } from "react-native"; -import { TaskAutomationValidationError } from "@/features/tasks/api"; import { AutomationDetail } from "@/features/tasks/components/AutomationDetail"; import { AutomationForm } from "@/features/tasks/components/AutomationForm"; import { diff --git a/apps/mobile/src/app/automation/create.tsx b/apps/mobile/src/app/automation/create.tsx index 2233cd688a..1f4d662bff 100644 --- a/apps/mobile/src/app/automation/create.tsx +++ b/apps/mobile/src/app/automation/create.tsx @@ -1,3 +1,4 @@ +import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; import { getCalendars } from "expo-localization"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useMemo, useRef, useState } from "react"; @@ -10,7 +11,6 @@ import { View, } from "react-native"; import { Text } from "@/components/text"; -import { TaskAutomationValidationError } from "@/features/tasks/api"; import { AutomationForm } from "@/features/tasks/components/AutomationForm"; import { useCreateTaskAutomation } from "@/features/tasks/hooks/useAutomations"; import { useSkillStoreSkill } from "@/features/tasks/skills/hooks"; diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index 889c7811ea..fe73a847f3 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -1,4 +1,10 @@ import { Text } from "@components/text"; +import { + countUserMessages, + getSessionActivityPhase, +} from "@posthog/core/sessions/sessionActivity"; +import { isTaskRunning } from "@posthog/core/tasks/taskArchive"; +import type { Task } from "@posthog/shared"; import { useQueryClient } from "@tanstack/react-query"; import * as Haptics from "expo-haptics"; import { useLocalSearchParams, useRouter } from "expo-router"; @@ -14,7 +20,7 @@ import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller import Animated, { useAnimatedStyle } from "react-native-reanimated"; import { FloatingBackButton } from "@/components/FloatingBackButton"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { getTask, runTaskInCloud } from "@/features/tasks/api"; +import { runTaskInCloud } from "@/features/tasks/api"; import { CustomImageBadge } from "@/features/tasks/components/CustomImageBadge"; import { FloatingTaskHeader } from "@/features/tasks/components/FloatingTaskHeader"; import { PrDiffStatsBadge } from "@/features/tasks/components/PrDiffStatsBadge"; @@ -51,15 +57,7 @@ import { } from "@/features/tasks/stores/pendingTaskPromptStore"; import { useTaskSessionStore } from "@/features/tasks/stores/taskSessionStore"; import { useTaskStore } from "@/features/tasks/stores/taskStore"; -import type { Task } from "@/features/tasks/types"; -import { - confirmStopRun, - isTaskRunning, -} from "@/features/tasks/utils/archiveGuard"; -import { - countUserMessages, - getSessionActivityPhase, -} from "@/features/tasks/utils/sessionActivity"; +import { confirmStopRun } from "@/features/tasks/utils/archiveGuard"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { ANALYTICS_EVENTS, @@ -67,6 +65,7 @@ import { useAnalytics, } from "@/lib/analytics"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useThemeColors } from "@/lib/theme"; const log = logger.scope("task-detail"); @@ -221,7 +220,8 @@ export default function TaskDetailScreen() { setLoading(true); setError(null); - getTask(taskId) + getPostHogApiClient() + .getTask(taskId) .then((fetchedTask) => { if (cancelled) return; setTask(fetchedTask); @@ -252,7 +252,8 @@ export default function TaskDetailScreen() { if (retrying) return; let cancelled = false; - getTask(taskId) + getPostHogApiClient() + .getTask(taskId) .then((freshTask) => { if (cancelled) return; setTask(freshTask); diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index 75fe35e96a..012b206dcc 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -30,7 +30,7 @@ import { import Animated, { runOnJS, useAnimatedStyle } from "react-native-reanimated"; import { useVoiceRecording } from "@/features/chat"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { createTask, runTaskInCloud } from "@/features/tasks/api"; +import { runTaskInCloud } from "@/features/tasks/api"; import { GitHubConnectionPrompt } from "@/features/tasks/components/GitHubConnectionPrompt"; import { GitHubLoadNotice } from "@/features/tasks/components/GitHubLoadNotice"; import { AttachmentSheet } from "@/features/tasks/composer/attachments/AttachmentSheet"; @@ -80,6 +80,7 @@ import { } from "@/features/tasks/utils/repositorySelection"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { toRgba, useThemeColors } from "@/lib/theme"; const log = logger.scope("task-create"); @@ -308,7 +309,7 @@ export default function NewTaskScreen() { ? `Attached: ${attachments[0].fileName}` : `Attached ${attachments.length} files`); - const task = await createTask({ + const task = await getPostHogApiClient().createTask({ description: descriptionText, title: descriptionText.slice(0, 100), repository: selection.repository ?? undefined, diff --git a/apps/mobile/src/features/chat/components/ToolMessage.tsx b/apps/mobile/src/features/chat/components/ToolMessage.tsx index 479849a51b..3859504777 100644 --- a/apps/mobile/src/features/chat/components/ToolMessage.tsx +++ b/apps/mobile/src/features/chat/components/ToolMessage.tsx @@ -659,9 +659,12 @@ function CreateTaskPreview({ try { // Dynamic import to avoid circular dependency - const { createTask, runTaskInCloud } = await import("../../tasks/api"); + const [{ runTaskInCloud }, { getPostHogApiClient }] = await Promise.all([ + import("../../tasks/api"), + import("@/lib/posthogApiClient"), + ]); - const task = await createTask({ + const task = await getPostHogApiClient().createTask({ title: args.title, description: args.description, repository: args.repository, diff --git a/apps/mobile/src/features/inbox/api.ts b/apps/mobile/src/features/inbox/api.ts index ce7bbeec0e..9b37b82725 100644 --- a/apps/mobile/src/features/inbox/api.ts +++ b/apps/mobile/src/features/inbox/api.ts @@ -1,5 +1,4 @@ -import { HttpError } from "@/features/tasks/api"; -import { authedFetch, getBaseUrl, getProjectId } from "@/lib/api"; +import { authedFetch, getBaseUrl, getProjectId, HttpError } from "@/lib/api"; import { logger } from "@/lib/logger"; import type { DismissalReasonOptionValue } from "./constants"; diff --git a/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx b/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx index 2aec45975d..2e30695576 100644 --- a/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx +++ b/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx @@ -1,4 +1,8 @@ import { Text } from "@components/text"; +import { + buildReviewerOptions, + reviewerMatchesAvailable, +} from "@posthog/core/inbox/artefacts"; import { MagnifyingGlass } from "phosphor-react-native"; import { useMemo, useState } from "react"; import { @@ -14,7 +18,6 @@ import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; import { useAvailableSuggestedReviewers } from "../hooks/useInboxReports"; import type { AvailableSuggestedReviewer, SuggestedReviewer } from "../types"; -import { buildReviewerOptions, reviewerMatchesAvailable } from "../utils"; import { ReviewerOptionRow } from "./ReviewerOptionRow"; interface EditReviewersSheetProps { diff --git a/apps/mobile/src/features/inbox/components/ReviewerFilterSheet.tsx b/apps/mobile/src/features/inbox/components/ReviewerFilterSheet.tsx index d07faaffa1..44ac730daf 100644 --- a/apps/mobile/src/features/inbox/components/ReviewerFilterSheet.tsx +++ b/apps/mobile/src/features/inbox/components/ReviewerFilterSheet.tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import { buildReviewerOptions } from "@posthog/core/inbox/artefacts"; import { useMemo } from "react"; import { ActivityIndicator, @@ -12,7 +13,6 @@ import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; import { useAvailableSuggestedReviewers } from "../hooks/useInboxReports"; import { useInboxFilterStore } from "../stores/inboxFilterStore"; -import { buildReviewerOptions } from "../utils"; import { ReviewerOptionRow } from "./ReviewerOptionRow"; interface ReviewerFilterSheetProps { diff --git a/apps/mobile/src/features/inbox/components/ReviewerOptionRow.tsx b/apps/mobile/src/features/inbox/components/ReviewerOptionRow.tsx index a6f9c5c265..0c21699177 100644 --- a/apps/mobile/src/features/inbox/components/ReviewerOptionRow.tsx +++ b/apps/mobile/src/features/inbox/components/ReviewerOptionRow.tsx @@ -1,8 +1,11 @@ import { Text } from "@components/text"; +import { + type ReviewerOption, + reviewerOptionLabel, +} from "@posthog/core/inbox/artefacts"; import { Check } from "phosphor-react-native"; import { Image, Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; -import { type ReviewerOption, reviewerOptionLabel } from "../utils"; interface ReviewerOptionRowProps { reviewer: ReviewerOption; diff --git a/apps/mobile/src/features/inbox/components/SuggestedReviewers.tsx b/apps/mobile/src/features/inbox/components/SuggestedReviewers.tsx index 0536f1b364..d0eab2e25f 100644 --- a/apps/mobile/src/features/inbox/components/SuggestedReviewers.tsx +++ b/apps/mobile/src/features/inbox/components/SuggestedReviewers.tsx @@ -1,4 +1,9 @@ import { Text } from "@components/text"; +import { + orderSuggestedReviewers, + reviewerMatchesAvailable, + toSuggestedReviewerWriteContent, +} from "@posthog/core/inbox/artefacts"; import { Eye, Plus, X } from "phosphor-react-native"; import { useMemo, useState } from "react"; import { @@ -20,11 +25,6 @@ import type { SuggestedReviewer, SuggestedReviewersArtefact, } from "../types"; -import { - orderSuggestedReviewers, - reviewerMatchesAvailable, - toSuggestedReviewerWriteContent, -} from "../utils"; import { EditReviewersSheet } from "./EditReviewersSheet"; export type ReviewerActionExtra = Pick< diff --git a/apps/mobile/src/features/inbox/components/TinderView.tsx b/apps/mobile/src/features/inbox/components/TinderView.tsx index cc034d6ceb..a8c99da1e4 100644 --- a/apps/mobile/src/features/inbox/components/TinderView.tsx +++ b/apps/mobile/src/features/inbox/components/TinderView.tsx @@ -17,7 +17,7 @@ import { } from "react-native-safe-area-context"; import { MarkdownText } from "@/features/chat/components/MarkdownText"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { createTask, runTaskInCloud } from "@/features/tasks/api"; +import { runTaskInCloud } from "@/features/tasks/api"; import { DEFAULT_MODEL } from "@/features/tasks/composer/options"; import type { CreateTaskOptions, @@ -29,6 +29,7 @@ import { useAnalytics, } from "@/lib/analytics"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useThemeColors } from "@/lib/theme"; import { getReportRepository } from "../api"; import { useDismissedReportsStore } from "../stores/dismissedReportsStore"; @@ -239,7 +240,7 @@ export function TinderView({ // 3. Create the task const prompt = `Act on this signal report. Investigate the root cause, implement the fix, and open a PR if appropriate.\n\n${report.summary ?? ""}`; - const task = await createTask({ + const task = await getPostHogApiClient().createTask({ description: prompt, title: prompt.slice(0, 255), repository: match?.repository ?? repo ?? undefined, diff --git a/apps/mobile/src/features/inbox/constants.ts b/apps/mobile/src/features/inbox/constants.ts index 201d15982f..f15aca7c5b 100644 --- a/apps/mobile/src/features/inbox/constants.ts +++ b/apps/mobile/src/features/inbox/constants.ts @@ -1,17 +1,3 @@ -/** Comma-separated statuses for the inbox pipeline (excludes terminal/deleted). */ -export const INBOX_PIPELINE_STATUS_FILTER = - "potential,candidate,in_progress,ready,pending_input"; - -/** - * Status filter for the Archive view — the two terminal, not-in-inbox states: - * `suppressed` (user archived it; restorable) and `resolved` (its - * implementation PR merged; terminal, reference only). - */ -export const INBOX_DISMISSED_STATUS_FILTER = "suppressed,resolved"; - -/** Polling interval for inbox queries (ms). */ -export const INBOX_REFETCH_INTERVAL_MS = 5_000; - /** * Reasons offered when the user dismisses a signal report. * Mirrors apps/code/src/shared/dismissalReasons.ts. diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts index 25f6e0eb40..ac37c738d1 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts @@ -1,3 +1,12 @@ +import { + buildArchiveListOrdering, + buildPriorityFilterParam, + buildSignalReportListOrdering, + buildStatusFilterParam, + buildSuggestedReviewerFilterParam, + INBOX_DISMISSED_STATUS_FILTER, + INBOX_REFETCH_INTERVAL_MS, +} from "@posthog/core/inbox/reportFiltering"; import { useInfiniteQuery, useMutation, @@ -19,10 +28,6 @@ import { restoreSignalReport, updateSignalReportArtefact, } from "../api"; -import { - INBOX_DISMISSED_STATUS_FILTER, - INBOX_REFETCH_INTERVAL_MS, -} from "../constants"; import { useInboxFilterStore } from "../stores/inboxFilterStore"; import type { AvailableSuggestedReviewersResponse, @@ -36,14 +41,7 @@ import type { SuggestedReviewer, SuggestedReviewerWriteEntry, } from "../types"; -import { - buildArchiveListOrdering, - buildPriorityFilterParam, - buildSignalReportListOrdering, - buildStatusFilterParam, - buildSuggestedReviewerFilterParam, - isRestorableReport, -} from "../utils"; +import { isRestorableReport } from "../utils"; export const inboxKeys = { all: ["inbox", "signal-reports"] as const, diff --git a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts index 97c16ccc3f..a0536417da 100644 --- a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts +++ b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts @@ -1,3 +1,4 @@ +import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering"; import type { SourceProduct } from "@posthog/shared"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; @@ -18,12 +19,7 @@ type SortDirection = "asc" | "desc"; export type { SourceProduct }; export const DEFAULT_STATUS_FILTER: SignalReportStatus[] = [ - "ready", - "pending_input", - "in_progress", - "failed", - "candidate", - "potential", + ...INBOX_PIPELINE_STATUSES, ]; interface InboxFilterState { diff --git a/apps/mobile/src/features/inbox/utils.test.ts b/apps/mobile/src/features/inbox/utils.test.ts index 8c1d70a32e..ad56b19305 100644 --- a/apps/mobile/src/features/inbox/utils.test.ts +++ b/apps/mobile/src/features/inbox/utils.test.ts @@ -1,25 +1,11 @@ import { describe, expect, it } from "vitest"; -import type { - AvailableSuggestedReviewer, - Signal, - SignalReport, - SignalReportOrderingField, - SignalReportStatus, - SuggestedReviewer, -} from "./types"; +import type { Signal, SignalReport, SignalReportStatus } from "./types"; import { - buildArchiveListOrdering, buildInboxViewedProperties, - buildPriorityFilterParam, - buildReviewerOptions, - buildSignalReportListOrdering, dismissalReasonLabel, formatSignalReportSummaryMarkdown, isRestorableReport, - orderSuggestedReviewers, - reviewerMatchesAvailable, sourceLine, - toSuggestedReviewerWriteContent, } from "./utils"; function signal(source_product: string, source_type: string): Signal { @@ -35,84 +21,6 @@ function signal(source_product: string, source_type: string): Signal { }; } -function reviewer(login: string, uuid?: string): SuggestedReviewer { - return { - github_login: login, - github_name: login, - relevant_commits: [], - user: uuid - ? { - id: 1, - uuid, - email: `${login}@posthog.com`, - first_name: login, - last_name: "", - } - : null, - }; -} - -describe("orderSuggestedReviewers", () => { - it("moves the current user to the front", () => { - const reviewers = [ - reviewer("a", "uuid-a"), - reviewer("me", "uuid-me"), - reviewer("c", "uuid-c"), - ]; - const ordered = orderSuggestedReviewers(reviewers, "uuid-me"); - expect(ordered.map((r) => r.github_login)).toEqual(["me", "a", "c"]); - }); - - it.each([ - { - label: "already first", - reviewers: [reviewer("me", "uuid-me"), reviewer("a", "uuid-a")], - meUuid: "uuid-me" as string | null | undefined, - }, - { - label: "absent", - reviewers: [reviewer("a", "uuid-a"), reviewer("b", "uuid-b")], - meUuid: "uuid-me" as string | null | undefined, - }, - { - label: "null meUuid", - reviewers: [reviewer("a", "uuid-a"), reviewer("me", "uuid-me")], - meUuid: null as string | null | undefined, - }, - { - label: "undefined meUuid", - reviewers: [reviewer("a", "uuid-a"), reviewer("me", "uuid-me")], - meUuid: undefined as string | null | undefined, - }, - ])("is a no-op when $label", ({ reviewers, meUuid }) => { - expect(orderSuggestedReviewers(reviewers, meUuid)).toBe(reviewers); - }); -}); - -function makeReviewer( - partial: Partial = {}, -): SuggestedReviewer { - return { - github_login: "octocat", - github_name: "The Octocat", - relevant_commits: [], - user: null, - ...partial, - }; -} - -function makeAvailable( - partial: Partial = {}, -): AvailableSuggestedReviewer { - return { - uuid: "uuid-1", - name: "Ada Lovelace", - email: "ada@example.com", - github_login: "ada", - ...partial, - }; -} - const DEFAULT_STATUS_FILTER: SignalReportStatus[] = [ "ready", "pending_input", @@ -299,139 +207,6 @@ describe("buildInboxViewedProperties", () => { }); }); -describe("toSuggestedReviewerWriteContent", () => { - it.each([ - { - name: "prefers github_login so the server preserves commits/name", - reviewer: makeReviewer({ - github_login: "ada", - user: { id: 1, uuid: "u1", email: "", first_name: "", last_name: "" }, - }), - expected: [{ github_login: "ada" }], - }, - { - name: "falls back to user_uuid when there is no github_login", - reviewer: makeReviewer({ - github_login: "", - user: { id: 1, uuid: "u1", email: "", first_name: "", last_name: "" }, - }), - expected: [{ user_uuid: "u1" }], - }, - { - name: "drops entries with neither a login nor a resolved user", - reviewer: makeReviewer({ github_login: "", user: null }), - expected: [], - }, - ])("$name", ({ reviewer, expected }) => { - expect(toSuggestedReviewerWriteContent([reviewer])).toEqual(expected); - }); -}); - -describe("reviewerMatchesAvailable", () => { - it.each([ - { - name: "matches on user uuid", - reviewer: makeReviewer({ - github_login: "", - user: { - id: 1, - uuid: "uuid-1", - email: "", - first_name: "", - last_name: "", - }, - }), - expected: true, - }, - { - name: "matches on case-insensitive github login", - reviewer: makeReviewer({ github_login: "ADA", user: null }), - expected: true, - }, - { - name: "does not match different people", - reviewer: makeReviewer({ github_login: "octocat", user: null }), - expected: false, - }, - ])("$name", ({ reviewer, expected }) => { - expect(reviewerMatchesAvailable(reviewer, makeAvailable())).toBe(expected); - }); -}); - -describe("buildSignalReportListOrdering", () => { - it.each([ - { - field: "priority" as SignalReportOrderingField, - direction: "desc" as const, - expected: "status,-is_suggested_reviewer,-priority,-created_at", - }, - { - field: "priority" as SignalReportOrderingField, - direction: "asc" as const, - expected: "status,-is_suggested_reviewer,priority,-created_at", - }, - { - field: "signal_count" as SignalReportOrderingField, - direction: "desc" as const, - expected: "status,-is_suggested_reviewer,-signal_count", - }, - { - field: "total_weight" as SignalReportOrderingField, - direction: "asc" as const, - expected: "status,-is_suggested_reviewer,total_weight", - }, - { - field: "created_at" as SignalReportOrderingField, - direction: "desc" as const, - expected: "status,-is_suggested_reviewer,-created_at", - }, - { - field: "updated_at" as SignalReportOrderingField, - direction: "asc" as const, - expected: "status,-is_suggested_reviewer,updated_at", - }, - ])( - "orders $field $direction as $expected", - ({ field, direction, expected }) => { - expect(buildSignalReportListOrdering(field, direction)).toBe(expected); - }, - ); -}); - -describe("buildPriorityFilterParam", () => { - it.each([ - { - name: "returns undefined for an empty selection", - input: [], - expected: undefined, - }, - { - name: "joins selected priorities with commas", - input: ["P0", "P2"] as const, - expected: "P0,P2", - }, - { - name: "dedupes repeated priorities", - input: ["P1", "P1", "P3"] as const, - expected: "P1,P3", - }, - ])("$name", ({ input, expected }) => { - expect(buildPriorityFilterParam([...input])).toBe(expected); - }); -}); - -describe("buildArchiveListOrdering", () => { - it.each([ - { direction: "desc" as const, expected: "-updated_at" }, - { direction: "asc" as const, expected: "updated_at" }, - ])( - "sorts by field without a status prefix ($direction)", - ({ direction, expected }) => { - expect(buildArchiveListOrdering("updated_at", direction)).toBe(expected); - }, - ); -}); - describe("isRestorableReport", () => { it.each([ { status: "suppressed" as SignalReportStatus, expected: true }, @@ -470,18 +245,3 @@ describe("sourceLine", () => { expect(sourceLine(signal(product, type))).toBe(expected); }); }); - -describe("buildReviewerOptions", () => { - it("dedupes by uuid and pins the current user first", () => { - const options = buildReviewerOptions( - [ - makeAvailable({ uuid: "b", name: "Bob" }), - makeAvailable({ uuid: "a", name: "Ada" }), - makeAvailable({ uuid: "a", name: "Ada (dupe)" }), - ], - "b", - ); - expect(options.map((o) => o.uuid)).toEqual(["b", "a"]); - expect(options[0].isMe).toBe(true); - }); -}); diff --git a/apps/mobile/src/features/inbox/utils.ts b/apps/mobile/src/features/inbox/utils.ts index 52ffb73972..b0040ddcef 100644 --- a/apps/mobile/src/features/inbox/utils.ts +++ b/apps/mobile/src/features/inbox/utils.ts @@ -6,14 +6,10 @@ import { differenceInHours, format, formatDistanceToNow } from "date-fns"; import type { InboxViewedProperties } from "@/lib/analytics"; import { DISMISSAL_REASON_OPTIONS } from "./constants"; import type { - AvailableSuggestedReviewer, Signal, SignalReport, - SignalReportOrderingField, SignalReportPriority, SignalReportStatus, - SuggestedReviewer, - SuggestedReviewerWriteEntry, } from "./types"; const ERROR_TRACKING_TYPE_LABELS: Record = { @@ -129,77 +125,6 @@ export function inboxStatusLabel(status: SignalReportStatus): string { } } -/** - * Build comma-separated `ordering` param for the API: - * 1. Status rank (ready first) - * 2. Suggested reviewer (current user first) - * 3. User-selected field - * - * Priority is a coarse 5-bucket rank, so ties are broken by newest first. - */ -export function buildSignalReportListOrdering( - field: SignalReportOrderingField, - direction: "asc" | "desc", -): string { - const fieldKey = direction === "desc" ? `-${field}` : field; - const tiebreak = field === "priority" ? ",-created_at" : ""; - return `status,-is_suggested_reviewer,${fieldKey}${tiebreak}`; -} - -/** - * Ordering for the Archive view, which lists two terminal statuses - * (`suppressed` + `resolved`). Unlike the pipeline ordering, it must not prefix - * with `status`: that would group one terminal state ahead of the other before - * the time sort, burying recent items behind older ones from the sibling status. - */ -export function buildArchiveListOrdering( - field: SignalReportOrderingField, - direction: "asc" | "desc", -): string { - return direction === "desc" ? `-${field}` : field; -} - -/** - * Build a comma-separated status filter string for the API. - */ -export function buildStatusFilterParam(statuses: SignalReportStatus[]): string { - return statuses.join(","); -} - -/** - * Build a comma-separated suggested reviewer filter for the API. - */ -export function buildSuggestedReviewerFilterParam( - reviewerIds: string[], -): string | undefined { - const normalized = reviewerIds.map((id) => id.trim()).filter(Boolean); - if (normalized.length === 0) return undefined; - return Array.from(new Set(normalized)).join(","); -} - -export function buildPriorityFilterParam( - priorities: SignalReportPriority[], -): string | undefined { - if (priorities.length === 0) return undefined; - return Array.from(new Set(priorities)).join(","); -} - -export function filterReportsBySearch( - reports: SignalReport[], - query: string, -): SignalReport[] { - const trimmed = query.trim(); - if (!trimmed) return reports; - - const lower = trimmed.toLowerCase(); - return reports.filter( - (report) => - report.title?.toLowerCase().includes(lower) || - report.summary?.toLowerCase().includes(lower) || - report.id.toLowerCase().includes(lower), - ); -} - /** * Returns only reports that are actionable for the tinder-like card deck: * ready, immediately actionable, not already addressed. @@ -213,91 +138,6 @@ export function getActionableReports(reports: SignalReport[]): SignalReport[] { ); } -export function orderSuggestedReviewers( - reviewers: SuggestedReviewer[], - meUuid: string | null | undefined, -): SuggestedReviewer[] { - if (!meUuid) return reviewers; - const meIndex = reviewers.findIndex((r) => r.user?.uuid === meUuid); - if (meIndex <= 0) return reviewers; - return [reviewers[meIndex], ...reviewers.filter((_, i) => i !== meIndex)]; -} - -export interface ReviewerOption { - uuid: string; - name: string; - email: string; - github_login: string; - isMe: boolean; -} - -/** Deduplicate the available-reviewers list by uuid and sort "Me" first, then by name. */ -export function buildReviewerOptions( - reviewers: AvailableSuggestedReviewer[], - currentUserUuid: string | undefined, -): ReviewerOption[] { - const seen = new Set(); - const options: ReviewerOption[] = []; - - for (const r of reviewers) { - if (!r.uuid || seen.has(r.uuid)) continue; - seen.add(r.uuid); - options.push({ - uuid: r.uuid, - name: r.name?.trim() || "", - email: r.email?.trim() || "", - github_login: r.github_login?.trim() || "", - isMe: r.uuid === currentUserUuid, - }); - } - - options.sort((a, b) => { - if (a.isMe && !b.isMe) return -1; - if (!a.isMe && b.isMe) return 1; - return (a.name || a.email).localeCompare(b.name || b.email); - }); - - return options; -} - -export function reviewerOptionLabel(r: ReviewerOption): string { - const base = r.name || r.email || "Unknown user"; - return r.isMe ? `${base} (Me)` : base; -} - -/** A reviewer in the artefact matches an org member by user uuid or (case-insensitive) login. */ -export function reviewerMatchesAvailable( - reviewer: SuggestedReviewer, - available: AvailableSuggestedReviewer, -): boolean { - if (reviewer.user?.uuid && reviewer.user.uuid === available.uuid) { - return true; - } - return ( - !!reviewer.github_login && - !!available.github_login && - reviewer.github_login.toLowerCase() === available.github_login.toLowerCase() - ); -} - -/** - * Build the full-replacement write payload from a read-shape list. Kept reviewers - * are sent by `github_login` so the server preserves their commits/name; an entry - * with only a resolved user falls back to `user_uuid`. Entries with neither are - * dropped. - */ -export function toSuggestedReviewerWriteContent( - reviewers: SuggestedReviewer[], -): SuggestedReviewerWriteEntry[] { - return reviewers - .map((reviewer): SuggestedReviewerWriteEntry | null => { - if (reviewer.github_login) return { github_login: reviewer.github_login }; - if (reviewer.user?.uuid) return { user_uuid: reviewer.user.uuid }; - return null; - }) - .filter((entry): entry is SuggestedReviewerWriteEntry => entry !== null); -} - interface InboxViewedFilterState { sourceProductFilter: string[]; statusFilter: SignalReportStatus[]; diff --git a/apps/mobile/src/features/navigation/components/SwipeableArchivedDrawerRow.tsx b/apps/mobile/src/features/navigation/components/SwipeableArchivedDrawerRow.tsx index 4bb2583879..6e942a2688 100644 --- a/apps/mobile/src/features/navigation/components/SwipeableArchivedDrawerRow.tsx +++ b/apps/mobile/src/features/navigation/components/SwipeableArchivedDrawerRow.tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import type { Task } from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { ArrowCounterClockwise } from "phosphor-react-native"; import { useEffect, useRef } from "react"; @@ -11,7 +12,6 @@ import { View, } from "react-native"; import { TaskStatusIcon } from "@/features/tasks/components/TaskStatusIcon"; -import type { Task } from "@/features/tasks/types"; import { useThemeColors } from "@/lib/theme"; const SWIPE_THRESHOLD = 60; diff --git a/apps/mobile/src/features/tasks/api.automations.test.ts b/apps/mobile/src/features/tasks/api.automations.test.ts deleted file mode 100644 index c4390a5d14..0000000000 --- a/apps/mobile/src/features/tasks/api.automations.test.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -const { mockFetch } = vi.hoisted(() => ({ - mockFetch: vi.fn(), -})); - -vi.mock("expo/fetch", () => ({ - fetch: mockFetch, -})); - -vi.mock("@/lib/api", () => ({ - getBaseUrl: () => "https://app.posthog.test", - getProjectId: () => 42, - authedFetch: (url: string, init?: RequestInit) => - mockFetch(url, { - ...init, - headers: { - Authorization: "Bearer token", - "Content-Type": "application/json", - ...((init?.headers as Record | undefined) ?? {}), - }, - }), -})); - -import { - createTaskAutomation, - deleteTaskAutomation, - getTaskAutomation, - getTaskAutomations, - runTaskAutomation, - TaskAutomationValidationError, - updateTaskAutomation, -} from "./api"; - -const automationPayload = { - id: "automation-1", - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - github_integration: 7, - cron_expression: "0 9 * * *", - timezone: "Europe/London", - template_id: "llm-skill:shared-daily-brief", - enabled: true, - last_run_at: null, - last_run_status: null, - last_task_id: "task-1", - last_task_run_id: null, - last_error: null, - created_at: "2026-05-13T00:00:00Z", - updated_at: "2026-05-13T00:00:00Z", -}; - -describe("task automation api", () => { - beforeEach(() => { - mockFetch.mockReset(); - }); - - it("lists task automations from the existing backend endpoint", async () => { - mockFetch.mockResolvedValueOnce( - new Response( - JSON.stringify({ - results: [automationPayload], - }), - { status: 200 }, - ), - ); - - const automations = await getTaskAutomations(); - - expect(automations).toHaveLength(1); - expect(automations[0]?.id).toBe("automation-1"); - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/task_automations/?limit=500", - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: "Bearer token", - }), - }), - ); - }); - - it("serializes automation creation payloads with the existing backend contract", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify(automationPayload), { status: 200 }), - ); - - await createTaskAutomation({ - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - github_integration: 7, - cron_expression: "0 9 * * *", - timezone: "Europe/London", - enabled: true, - template_id: "llm-skill:shared-daily-brief", - }); - - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/task_automations/", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - github_integration: 7, - cron_expression: "0 9 * * *", - timezone: "Europe/London", - enabled: true, - template_id: "llm-skill:shared-daily-brief", - }), - }), - ); - }); - - it("serializes skill-backed automation payloads with a prefixed template id", async () => { - mockFetch.mockResolvedValueOnce( - new Response( - JSON.stringify({ - ...automationPayload, - id: "automation-2", - name: "Shared daily brief", - template_id: "llm-skill:shared-daily-brief", - }), - { status: 200 }, - ), - ); - - await createTaskAutomation({ - name: "Shared daily brief", - prompt: "Summarize feature usage for my product areas.", - repository: "posthog/posthog", - github_integration: 7, - cron_expression: "0 8 * * 1-5", - timezone: "America/New_York", - enabled: true, - template_id: "llm-skill:shared-daily-brief", - }); - - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/task_automations/", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ - name: "Shared daily brief", - prompt: "Summarize feature usage for my product areas.", - repository: "posthog/posthog", - github_integration: 7, - cron_expression: "0 8 * * 1-5", - timezone: "America/New_York", - enabled: true, - template_id: "llm-skill:shared-daily-brief", - }), - }), - ); - }); - - it("retains backend field attribution for validation errors", async () => { - mockFetch.mockImplementation(() => - Promise.resolve( - new Response( - JSON.stringify({ - type: "validation_error", - code: "invalid_input", - detail: - "Only standard 5-field cron expressions are supported (minute hour day month weekday). Example: '0 9 * * 1-5'.", - attr: "cron_expression", - }), - { status: 400, statusText: "Bad Request" }, - ), - ), - ); - - await expect( - createTaskAutomation({ - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - cron_expression: "not a cron", - timezone: "Europe/London", - }), - ).rejects.toBeInstanceOf(TaskAutomationValidationError); - - await expect( - createTaskAutomation({ - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - cron_expression: "not a cron", - timezone: "Europe/London", - }), - ).rejects.toMatchObject({ - attr: "cron_expression", - code: "invalid_input", - }); - }); - - it("surfaces skill-backed validation failures without losing backend attr info", async () => { - mockFetch.mockResolvedValueOnce( - new Response( - JSON.stringify({ - type: "validation_error", - code: "invalid_input", - detail: "Repository is still required for this template.", - attr: "repository", - }), - { status: 400, statusText: "Bad Request" }, - ), - ); - - await expect( - createTaskAutomation({ - name: "Shared daily brief", - prompt: "Summarize feature usage for my product areas.", - repository: "", - github_integration: null, - cron_expression: "0 8 * * 1-5", - timezone: "America/New_York", - enabled: true, - template_id: "llm-skill:shared-daily-brief", - }), - ).rejects.toMatchObject({ - attr: "repository", - code: "invalid_input", - message: "Repository is still required for this template.", - }); - }); - - it("supports retrieve, update, delete, and run-now automation flows", async () => { - mockFetch - .mockResolvedValueOnce( - new Response(JSON.stringify(automationPayload), { status: 200 }), - ) - .mockResolvedValueOnce( - new Response(JSON.stringify(automationPayload), { status: 200 }), - ) - .mockResolvedValueOnce(new Response(null, { status: 204 })) - .mockResolvedValueOnce( - new Response(JSON.stringify(automationPayload), { status: 200 }), - ); - - const retrieved = await getTaskAutomation("automation-1"); - const updated = await updateTaskAutomation("automation-1", { - enabled: false, - cron_expression: "30 14 * * *", - }); - await deleteTaskAutomation("automation-1"); - const ran = await runTaskAutomation("automation-1"); - - expect(retrieved.id).toBe("automation-1"); - expect(updated.id).toBe("automation-1"); - expect(ran.id).toBe("automation-1"); - expect(mockFetch).toHaveBeenNthCalledWith( - 2, - "https://app.posthog.test/api/projects/42/task_automations/automation-1/", - expect.objectContaining({ - method: "PATCH", - body: JSON.stringify({ - enabled: false, - cron_expression: "30 14 * * *", - }), - }), - ); - expect(mockFetch).toHaveBeenNthCalledWith( - 3, - "https://app.posthog.test/api/projects/42/task_automations/automation-1/", - expect.objectContaining({ - method: "DELETE", - }), - ); - expect(mockFetch).toHaveBeenNthCalledWith( - 4, - "https://app.posthog.test/api/projects/42/task_automations/automation-1/run/", - expect.objectContaining({ - method: "POST", - }), - ); - }); -}); diff --git a/apps/mobile/src/features/tasks/api.test.ts b/apps/mobile/src/features/tasks/api.test.ts index ab54e96253..bcb3f5c1f0 100644 --- a/apps/mobile/src/features/tasks/api.test.ts +++ b/apps/mobile/src/features/tasks/api.test.ts @@ -1,7 +1,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -const { mockFetch } = vi.hoisted(() => ({ +const { mockFetch, mockRunTaskInCloud } = vi.hoisted(() => ({ mockFetch: vi.fn(), + mockRunTaskInCloud: vi.fn(), })); vi.mock("expo/fetch", () => ({ @@ -9,6 +10,16 @@ vi.mock("expo/fetch", () => ({ })); vi.mock("@/lib/api", () => ({ + HttpError: class HttpError extends Error { + constructor( + readonly status: number, + readonly statusText: string, + message: string, + ) { + super(message); + this.name = "HttpError"; + } + }, getBaseUrl: () => "https://app.posthog.test", getProjectId: () => 42, getAccessToken: () => "token", @@ -24,6 +35,10 @@ vi.mock("@/lib/api", () => ({ }), })); +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ runTaskInCloud: mockRunTaskInCloud }), +})); + import { cancelRun, HttpError, @@ -38,10 +53,8 @@ function bodyOf(call: unknown): Record { describe("runTaskInCloud", () => { beforeEach(() => { - mockFetch.mockReset(); - mockFetch.mockResolvedValue( - new Response(JSON.stringify({ id: "task-1" }), { status: 200 }), - ); + mockRunTaskInCloud.mockReset(); + mockRunTaskInCloud.mockResolvedValue({ id: "task-1" }); }); it.each([true, false])( @@ -49,23 +62,28 @@ describe("runTaskInCloud", () => { async (flag) => { await runTaskInCloud("task-1", { autoPublish: flag }); - expect(bodyOf(mockFetch.mock.calls[0])).toMatchObject({ - auto_publish: flag, - }); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ autoPublish: flag }), + ); }, ); it("omits auto_publish when not provided", async () => { await runTaskInCloud("task-1", { model: "claude-opus-4-8" }); - expect(bodyOf(mockFetch.mock.calls[0])).not.toHaveProperty("auto_publish"); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ autoPublish: undefined }), + ); }); it("sends no body for the plain initial run", async () => { await runTaskInCloud("task-1"); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - expect(init.body).toBeUndefined(); + expect(mockRunTaskInCloud).toHaveBeenCalledWith("task-1"); }); it("forwards the selected sandbox environment and custom image", async () => { @@ -74,10 +92,14 @@ describe("runTaskInCloud", () => { customImageId: "image-123", }); - expect(bodyOf(mockFetch.mock.calls[0])).toMatchObject({ - sandbox_environment_id: "environment-123", - custom_image_id: "image-123", - }); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ + sandboxEnvironmentId: "environment-123", + customImageId: "image-123", + }), + ); }); it("omits the sandbox environment and custom image when unset", async () => { @@ -87,24 +109,34 @@ describe("runTaskInCloud", () => { customImageId: null, }); - const body = bodyOf(mockFetch.mock.calls[0]); - expect(body).not.toHaveProperty("sandbox_environment_id"); - expect(body).not.toHaveProperty("custom_image_id"); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ + sandboxEnvironmentId: undefined, + customImageId: undefined, + }), + ); }); it("sends rtk_enabled=false when the run opts out", async () => { await runTaskInCloud("task-1", { rtkEnabled: false }); - expect(bodyOf(mockFetch.mock.calls[0])).toMatchObject({ - rtk_enabled: false, - }); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ rtkEnabled: false }), + ); }); it("omits rtk_enabled when the run keeps compression on", async () => { await runTaskInCloud("task-1", { rtkEnabled: true }); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - expect(init.body).toBeUndefined(); + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "task-1", + undefined, + expect.objectContaining({ rtkEnabled: true }), + ); }); }); diff --git a/apps/mobile/src/features/tasks/api.ts b/apps/mobile/src/features/tasks/api.ts index f7d03d1056..f50a155246 100644 --- a/apps/mobile/src/features/tasks/api.ts +++ b/apps/mobile/src/features/tasks/api.ts @@ -1,8 +1,10 @@ -import type { Adapter } from "@posthog/shared"; import type { - SandboxCustomImage, - SandboxEnvironment, -} from "@posthog/shared/domain-types"; + Adapter, + ExecutionMode, + StoredLogEntry, + Task, + TaskRun, +} from "@posthog/shared"; import { fetch } from "expo/fetch"; import { authedFetch, @@ -10,75 +12,11 @@ import { getAccessToken, getBaseUrl, getProjectId, + HttpError, } from "@/lib/api"; -import { logger } from "@/lib/logger"; -import type { - CreateTaskAutomationOptions, - CreateTaskOptions, - Integration, - StoredLogEntry, - Task, - TaskAutomation, - TaskRun, - UpdateTaskAutomationOptions, - UserGithubIntegration, -} from "./types"; - -const log = logger.scope("tasks-api"); - -export class HttpError extends Error { - readonly status: number; - - constructor(status: number, statusText: string, prefix: string) { - super(`${prefix}: ${status} ${statusText}`); - this.name = "HttpError"; - this.status = status; - } -} - -export class TaskAutomationValidationError extends Error { - readonly code: string; - readonly attr: string | null; - - constructor(message: string, code: string, attr: string | null) { - super(message); - this.name = "TaskAutomationValidationError"; - this.code = code; - this.attr = attr; - } -} - -async function parseJsonResponse(response: Response): Promise { - return (await response.json()) as T; -} +import { getPostHogApiClient } from "@/lib/posthogApiClient"; -async function parseTaskAutomationError(response: Response): Promise { - let payload: { - code?: string; - detail?: string; - attr?: string; - } | null = null; - - try { - payload = await response.json(); - } catch { - payload = null; - } - - if (response.status === 400 && payload?.detail) { - throw new TaskAutomationValidationError( - payload.detail, - payload.code ?? "invalid_input", - payload.attr ?? null, - ); - } - - throw new HttpError( - response.status, - response.statusText, - "Task automation request failed", - ); -} +export { HttpError } from "@/lib/api"; async function withRetry( fn: () => Promise, @@ -125,309 +63,6 @@ function isRetryableError(error: unknown): boolean { return false; } -export async function getTasks(filters?: { - repository?: string; - createdBy?: number; - originProduct?: string; -}): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const params = new URLSearchParams({ limit: "500" }); - if (filters?.repository) { - params.set("repository", filters.repository); - } - if (filters?.createdBy) { - params.set("created_by", String(filters.createdBy)); - } - if (filters?.originProduct) { - params.set("origin_product", filters.originProduct); - } - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/?${params}`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch tasks", - ); - } - - const data = await parseJsonResponse<{ results?: Task[] }>(response); - return data.results ?? []; -} - -export async function getTask(taskId: string): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/${taskId}/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch task", - ); - } - - return await parseJsonResponse(response); -} - -export async function getTaskAutomations(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/?limit=500`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch task automations", - ); - } - - const data = await parseJsonResponse<{ results?: TaskAutomation[] }>( - response, - ); - return data.results ?? []; -} - -export async function getTaskAutomation( - automationId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/${automationId}/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch task automation", - ); - } - - return await parseJsonResponse(response); -} - -export async function createTaskAutomation( - options: CreateTaskAutomationOptions, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/`, - { - method: "POST", - body: JSON.stringify(options), - }, - ); - - if (!response.ok) { - await parseTaskAutomationError(response); - } - - return await parseJsonResponse(response); -} - -export async function updateTaskAutomation( - automationId: string, - updates: UpdateTaskAutomationOptions, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/${automationId}/`, - { - method: "PATCH", - body: JSON.stringify(updates), - }, - ); - - if (!response.ok) { - await parseTaskAutomationError(response); - } - - return await parseJsonResponse(response); -} - -export async function deleteTaskAutomation( - automationId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/${automationId}/`, - { method: "DELETE" }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to delete task automation", - ); - } -} - -export async function runTaskAutomation( - automationId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/task_automations/${automationId}/run/`, - { method: "POST" }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to run task automation", - ); - } - - return await parseJsonResponse(response); -} - -export async function warmTask(options: { - repository: string; - github_integration: number; - branch?: string | null; - runtime_adapter?: string | null; - model?: string | null; - reasoning_effort?: string | null; - sandbox_environment_id?: string | null; - custom_image_id?: string | null; -}): Promise<{ task_id: string; run_id: string } | null> { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/warm/`, - { - method: "POST", - body: JSON.stringify({ - repository: options.repository, - github_integration: options.github_integration, - branch: options.branch ?? null, - runtime_adapter: options.runtime_adapter ?? null, - model: options.model ?? null, - reasoning_effort: options.reasoning_effort ?? null, - ...(options.sandbox_environment_id - ? { sandbox_environment_id: options.sandbox_environment_id } - : {}), - ...(options.custom_image_id - ? { custom_image_id: options.custom_image_id } - : {}), - }), - }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to warm task", - ); - } - - const text = await response.text(); - if (!text) { - return null; - } - return JSON.parse(text) as { task_id: string; run_id: string }; -} - -export async function createTask(options: CreateTaskOptions): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/`, - { - method: "POST", - body: JSON.stringify({ - origin_product: "user_created", - ...options, - }), - }, - ); - - if (!response.ok) { - const errorText = await response.text(); - log.error("Create task error", errorText); - throw new HttpError( - response.status, - `${response.statusText} - ${errorText}`, - "Failed to create task", - ); - } - - return await parseJsonResponse(response); -} - -export async function updateTask( - taskId: string, - updates: Partial, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/${taskId}/`, - { - method: "PATCH", - body: JSON.stringify(updates), - }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to update task", - ); - } - - return await parseJsonResponse(response); -} - -export async function deleteTask(taskId: string): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/${taskId}/`, - { method: "DELETE" }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to delete task", - ); - } -} - export interface RunTaskInCloudOptions { branch?: string | null; resumeFromRunId?: string; @@ -439,10 +74,6 @@ export interface RunTaskInCloudOptions { model?: string; /** Reasoning effort: "low" | "medium" | "high" (model-dependent). */ reasoningEffort?: string; - /** Sandbox environment / custom base image to run on. Sent so a reused warm - * sandbox matches the selection instead of a mismatched default. */ - sandboxEnvironmentId?: string | null; - customImageId?: string | null; /** Permission mode: "default" | "acceptEdits" | "plan" | "auto". */ initialPermissionMode?: string; /** Source that triggered this run. */ @@ -454,92 +85,34 @@ export interface RunTaskInCloudOptions { autoPublish?: boolean; /** Only false is sent: opts the run out of rtk command-output compression. */ rtkEnabled?: boolean; + sandboxEnvironmentId?: string | null; + customImageId?: string | null; } export async function runTaskInCloud( taskId: string, options?: RunTaskInCloudOptions, ): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - // Only serialize a body when we have options to send. Sending an empty - // or minimal body on the initial run historically changed backend - // behavior, so we preserve the "no body" path for the common case. - const hasOptions = - !!options && - (options.branch !== undefined || - options.resumeFromRunId !== undefined || - options.pendingUserMessage !== undefined || - options.mode !== undefined || - options.runtimeAdapter !== undefined || - options.model !== undefined || - options.reasoningEffort !== undefined || - options.sandboxEnvironmentId !== undefined || - options.customImageId !== undefined || - options.initialPermissionMode !== undefined || - options.runSource !== undefined || - options.signalReportId !== undefined || - options.autoPublish !== undefined || - options.rtkEnabled === false); - - let body: string | undefined; - if (hasOptions) { - const payload: Record = { - mode: options?.mode ?? "interactive", - }; - if (options?.branch) payload.branch = options.branch; - if (options?.resumeFromRunId) { - payload.resume_from_run_id = options.resumeFromRunId; - } - if (options?.pendingUserMessage) { - payload.pending_user_message = options.pendingUserMessage; - } - if (options?.runtimeAdapter) { - payload.runtime_adapter = options.runtimeAdapter; - if (options?.model) payload.model = options.model; - if (options?.reasoningEffort) { - payload.reasoning_effort = options.reasoningEffort; - } - } - if (options?.sandboxEnvironmentId) { - payload.sandbox_environment_id = options.sandboxEnvironmentId; - } - if (options?.customImageId) { - payload.custom_image_id = options.customImageId; - } - if (options?.initialPermissionMode) { - payload.initial_permission_mode = options.initialPermissionMode; - } - if (options?.runSource) payload.run_source = options.runSource; - if (options?.signalReportId) - payload.signal_report_id = options.signalReportId; - if (options?.autoPublish !== undefined) { - payload.auto_publish = options.autoPublish; - } - if (options?.rtkEnabled === false) { - payload.rtk_enabled = false; - } - body = JSON.stringify(payload); - } - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/tasks/${taskId}/run/`, - { - method: "POST", - body, - }, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to run task", - ); - } - - return await response.json(); + if (!options) { + return getPostHogApiClient().runTaskInCloud(taskId); + } + + return getPostHogApiClient().runTaskInCloud(taskId, options.branch, { + adapter: options.runtimeAdapter, + model: options.model, + reasoningLevel: options.reasoningEffort, + initialPermissionMode: options.initialPermissionMode as + | ExecutionMode + | undefined, + runSource: options.runSource, + signalReportId: options.signalReportId, + autoPublish: options.autoPublish, + rtkEnabled: options.rtkEnabled, + sandboxEnvironmentId: options.sandboxEnvironmentId ?? undefined, + customImageId: options.customImageId ?? undefined, + resumeFromRunId: options.resumeFromRunId, + pendingUserMessage: options.pendingUserMessage, + }); } export async function getTaskRun( @@ -834,232 +407,3 @@ export async function streamCloudTask( signal: options.signal, }); } - -export async function getSandboxCustomImages(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/sandbox_custom_images/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch sandbox custom images", - ); - } - - const data = await parseJsonResponse<{ results?: SandboxCustomImage[] }>( - response, - ); - return data.results ?? []; -} - -export async function getSandboxEnvironments(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/sandbox_environments/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch sandbox environments", - ); - } - - const data = await parseJsonResponse<{ results?: SandboxEnvironment[] }>( - response, - ); - return data.results ?? []; -} - -export async function getIntegrations(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/environments/${projectId}/integrations/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch integrations", - ); - } - - const data = await parseJsonResponse< - | { - results?: Integration[]; - } - | Integration[] - >(response); - return Array.isArray(data) ? data : (data.results ?? []); -} - -const GITHUB_REPOS_PAGE_SIZE = 500; - -export async function getGithubRepositories( - integrationId: number, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const allRepos: string[] = []; - let offset = 0; - - while (true) { - const params = new URLSearchParams({ - limit: String(GITHUB_REPOS_PAGE_SIZE), - offset: String(offset), - }); - const response = await authedFetch( - `${baseUrl}/api/environments/${projectId}/integrations/${integrationId}/github_repos/?${params}`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch repositories", - ); - } - - const data = await response.json(); - const repos: Array = - data.repositories ?? data.results ?? data ?? []; - - const normalized = repos - .map((repo) => { - if (typeof repo === "string") return repo.toLowerCase(); - return (repo.full_name ?? repo.name ?? "").toLowerCase(); - }) - .filter((name) => name.length > 0); - - allRepos.push(...normalized); - - if (!data.has_more || repos.length === 0) { - return allRepos; - } - - offset += repos.length; - } -} - -export interface GithubUserConnectResult { - install_url: string; - connect_flow?: "oauth_authorize" | "oauth_discover" | "app_install"; -} - -/** - * Starts the user-scoped GitHub connection flow (mirrors desktop). The backend - * picks the lightweight OAuth flow when the team already has the GitHub App - * installed, otherwise a discover/install flow, and returns the URL to open. - * - * `connect_from: "posthog_mobile"` tells the backend to redirect the OAuth - * callback to `posthog://github/callback` so the in-app browser auto-closes. - */ -export async function startGithubUserIntegrationConnect(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/users/@me/integrations/github/start/`, - { - method: "POST", - body: JSON.stringify({ - team_id: projectId, - connect_from: "posthog_mobile", - }), - }, - ); - - if (!response.ok) { - const payload = (await response.json().catch(() => ({}))) as { - detail?: unknown; - }; - const detail = - typeof payload.detail === "string" - ? payload.detail - : "Failed to start GitHub connection"; - throw new HttpError(response.status, response.statusText, detail); - } - - return parseJsonResponse(response); -} - -export async function getUserGithubIntegrations(): Promise< - UserGithubIntegration[] -> { - const baseUrl = getBaseUrl(); - - const response = await authedFetch( - `${baseUrl}/api/users/@me/integrations/?kind=github`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch personal GitHub integrations", - ); - } - - const data = await parseJsonResponse<{ results?: UserGithubIntegration[] }>( - response, - ); - return data.results ?? []; -} - -export async function getUserGithubRepositories( - installationId: string, -): Promise { - const baseUrl = getBaseUrl(); - - const allRepos: string[] = []; - let offset = 0; - - while (true) { - const params = new URLSearchParams({ - limit: String(GITHUB_REPOS_PAGE_SIZE), - offset: String(offset), - }); - const response = await authedFetch( - `${baseUrl}/api/users/@me/integrations/github/${installationId}/repos/?${params}`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch repositories", - ); - } - - const data = await response.json(); - const repos: Array = - data.repositories ?? data.results ?? data ?? []; - - const normalized = repos - .map((repo) => { - if (typeof repo === "string") return repo.toLowerCase(); - return (repo.full_name ?? repo.name ?? "").toLowerCase(); - }) - .filter((name) => name.length > 0); - - allRepos.push(...normalized); - - if (!data.has_more || repos.length === 0) { - return allRepos; - } - - offset += repos.length; - } -} diff --git a/apps/mobile/src/features/tasks/api.warm.test.ts b/apps/mobile/src/features/tasks/api.warm.test.ts deleted file mode 100644 index 06c9d1aba0..0000000000 --- a/apps/mobile/src/features/tasks/api.warm.test.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -const { mockFetch } = vi.hoisted(() => ({ - mockFetch: vi.fn(), -})); - -vi.mock("expo/fetch", () => ({ - fetch: mockFetch, -})); - -vi.mock("@/lib/api", () => ({ - getBaseUrl: () => "https://app.posthog.test", - getProjectId: () => 42, - authedFetch: (url: string, init?: RequestInit) => - mockFetch(url, { - ...init, - headers: { - Authorization: "Bearer token", - "Content-Type": "application/json", - ...((init?.headers as Record | undefined) ?? {}), - }, - }), -})); - -import { HttpError, warmTask } from "./api"; - -describe("warmTask", () => { - beforeEach(() => { - mockFetch.mockReset(); - }); - - it("posts the warm request with the backend contract", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify({ task_id: "task-1", run_id: "run-1" }), { - status: 200, - }), - ); - - const result = await warmTask({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - }); - - expect(result).toEqual({ task_id: "task-1", run_id: "run-1" }); - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/tasks/warm/", - expect.objectContaining({ - method: "POST", - body: JSON.stringify({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - runtime_adapter: null, - model: null, - reasoning_effort: null, - }), - }), - ); - }); - - it("forwards the selected runtime, model, and reasoning effort", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify({ task_id: "task-1", run_id: "run-1" }), { - status: 200, - }), - ); - - await warmTask({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - runtime_adapter: "claude", - model: "claude-opus-4-8", - reasoning_effort: "high", - }); - - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/tasks/warm/", - expect.objectContaining({ - body: JSON.stringify({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - runtime_adapter: "claude", - model: "claude-opus-4-8", - reasoning_effort: "high", - }), - }), - ); - }); - - it("forwards the selected sandbox environment and custom image", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify({ task_id: "task-1", run_id: "run-1" }), { - status: 200, - }), - ); - - await warmTask({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - sandbox_environment_id: "environment-123", - custom_image_id: "image-123", - }); - - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/tasks/warm/", - expect.objectContaining({ - body: JSON.stringify({ - repository: "posthog/posthog", - github_integration: 7, - branch: "main", - runtime_adapter: null, - model: null, - reasoning_effort: null, - sandbox_environment_id: "environment-123", - custom_image_id: "image-123", - }), - }), - ); - }); - - it("omits the sandbox environment and custom image when unset", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify({ task_id: "task-1", run_id: "run-1" }), { - status: 200, - }), - ); - - await warmTask({ - repository: "posthog/posthog", - github_integration: 7, - sandbox_environment_id: null, - custom_image_id: null, - }); - - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body).not.toHaveProperty("sandbox_environment_id"); - expect(body).not.toHaveProperty("custom_image_id"); - }); - - it("serializes a missing branch as null", async () => { - mockFetch.mockResolvedValueOnce( - new Response(JSON.stringify({ task_id: "task-1", run_id: "run-1" }), { - status: 200, - }), - ); - - await warmTask({ repository: "posthog/posthog", github_integration: 7 }); - - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/projects/42/tasks/warm/", - expect.objectContaining({ - body: JSON.stringify({ - repository: "posthog/posthog", - github_integration: 7, - branch: null, - runtime_adapter: null, - model: null, - reasoning_effort: null, - }), - }), - ); - }); - - it("returns null when the response body is empty", async () => { - mockFetch.mockResolvedValueOnce(new Response("", { status: 200 })); - - const result = await warmTask({ - repository: "posthog/posthog", - github_integration: 7, - }); - - expect(result).toBeNull(); - }); - - it("throws an HttpError on a failed response", async () => { - mockFetch.mockResolvedValueOnce(new Response("nope", { status: 500 })); - - await expect( - warmTask({ repository: "posthog/posthog", github_integration: 7 }), - ).rejects.toBeInstanceOf(HttpError); - }); -}); diff --git a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx index 6838b40761..b560293b6b 100644 --- a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx @@ -1,7 +1,8 @@ import { Text } from "@components/text"; +import type { TaskAutomation } from "@posthog/api-client/posthog-client"; +import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; +import type { TaskRun } from "@posthog/shared"; import { ActivityIndicator, Pressable, View } from "react-native"; -import type { TaskAutomation, TaskRun } from "../types"; -import { formatAutomationScheduleSummary } from "../utils/automationSchedule"; import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; diff --git a/apps/mobile/src/features/tasks/components/AutomationForm.tsx b/apps/mobile/src/features/tasks/components/AutomationForm.tsx index d6f076ef82..56387f474d 100644 --- a/apps/mobile/src/features/tasks/components/AutomationForm.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationForm.tsx @@ -1,4 +1,12 @@ import { Text } from "@components/text"; +import type { CreateTaskAutomationOptions } from "@posthog/api-client/posthog-client"; +import { + type AutomationScheduleDraft, + buildCronExpression, + createDefaultScheduleDraft, + deriveAutomationName, + parseCronExpression, +} from "@posthog/core/automations/automationSchedule"; import { CaretDown, GithubLogo } from "phosphor-react-native"; import { type MutableRefObject, useEffect, useMemo, useState } from "react"; import { @@ -12,17 +20,7 @@ import { MarkdownText } from "@/features/chat/components/MarkdownText"; import { useThemeColors } from "@/lib/theme"; import { RepositoryPickerInline } from "../composer/RepositoryPickerInline"; import { useIntegrations } from "../hooks/useIntegrations"; -import type { - CreateTaskAutomationOptions, - RepositorySelection, -} from "../types"; -import { - type AutomationScheduleDraft, - buildCronExpression, - createDefaultScheduleDraft, - deriveAutomationName, - parseCronExpression, -} from "../utils/automationSchedule"; +import type { RepositorySelection } from "../types"; import { findRepositoryOption, isRepositorySelectionComplete, diff --git a/apps/mobile/src/features/tasks/components/AutomationItem.tsx b/apps/mobile/src/features/tasks/components/AutomationItem.tsx index 5ce8a7fe80..89d4f4d5de 100644 --- a/apps/mobile/src/features/tasks/components/AutomationItem.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationItem.tsx @@ -1,9 +1,10 @@ import { Text } from "@components/text"; +import type { TaskAutomation } from "@posthog/api-client/posthog-client"; +import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; +import type { TaskRun } from "@posthog/shared"; import { format, formatDistanceToNow } from "date-fns"; import { memo } from "react"; import { Pressable, View } from "react-native"; -import type { TaskAutomation, TaskRun } from "../types"; -import { formatAutomationScheduleSummary } from "../utils/automationSchedule"; import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; diff --git a/apps/mobile/src/features/tasks/components/AutomationList.tsx b/apps/mobile/src/features/tasks/components/AutomationList.tsx index 31a5821962..1027a5aa46 100644 --- a/apps/mobile/src/features/tasks/components/AutomationList.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationList.tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { Plus } from "phosphor-react-native"; import { ActivityIndicator, @@ -10,7 +11,6 @@ import { import { useThemeColors } from "@/lib/theme"; import { useAutomations } from "../hooks/useAutomations"; import { useTasks } from "../hooks/useTasks"; -import type { TaskAutomation } from "../types"; import { AutomationItem } from "./AutomationItem"; interface AutomationListProps { diff --git a/apps/mobile/src/features/tasks/components/AutomationStatusBadge.tsx b/apps/mobile/src/features/tasks/components/AutomationStatusBadge.tsx index 970de00d64..600b36302e 100644 --- a/apps/mobile/src/features/tasks/components/AutomationStatusBadge.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationStatusBadge.tsx @@ -1,6 +1,6 @@ import { Text } from "@components/text"; +import type { TaskRun } from "@posthog/shared"; import { View } from "react-native"; -import type { TaskRun } from "../types"; import { getAutomationStatusPresentation } from "../utils/automationStatus"; interface AutomationStatusBadgeProps { diff --git a/apps/mobile/src/features/tasks/components/CreateAutomationScreen.test.tsx b/apps/mobile/src/features/tasks/components/CreateAutomationScreen.test.tsx index 29934cada1..87fb997014 100644 --- a/apps/mobile/src/features/tasks/components/CreateAutomationScreen.test.tsx +++ b/apps/mobile/src/features/tasks/components/CreateAutomationScreen.test.tsx @@ -60,7 +60,7 @@ vi.mock("@/features/tasks/components/AutomationForm", () => ({ createElement("AutomationForm", props), })); -vi.mock("@/features/tasks/api", () => ({ +vi.mock("@posthog/api-client/posthog-client", () => ({ TaskAutomationValidationError: class TaskAutomationValidationError extends Error { code: string; attr: string | null; diff --git a/apps/mobile/src/features/tasks/components/CustomImageBadge.test.tsx b/apps/mobile/src/features/tasks/components/CustomImageBadge.test.tsx index 09b0531b6c..cc6d38b3bd 100644 --- a/apps/mobile/src/features/tasks/components/CustomImageBadge.test.tsx +++ b/apps/mobile/src/features/tasks/components/CustomImageBadge.test.tsx @@ -1,8 +1,8 @@ +import type { Task, TaskRun } from "@posthog/shared"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createElement } from "react"; import { act, create } from "react-test-renderer"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import type { Task, TaskRun } from "../types"; const { mockUseAuthStore, mockGetImages, mockGetEnvironments } = vi.hoisted( () => ({ @@ -14,9 +14,11 @@ const { mockUseAuthStore, mockGetImages, mockGetEnvironments } = vi.hoisted( vi.mock("@/features/auth", () => ({ useAuthStore: mockUseAuthStore })); -vi.mock("../api", () => ({ - getSandboxCustomImages: mockGetImages, - getSandboxEnvironments: mockGetEnvironments, +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ + listSandboxCustomImages: mockGetImages, + listSandboxEnvironments: mockGetEnvironments, + }), })); vi.mock("phosphor-react-native", () => ({ diff --git a/apps/mobile/src/features/tasks/components/CustomImageBadge.tsx b/apps/mobile/src/features/tasks/components/CustomImageBadge.tsx index 2e382c228b..13bf4e2d4a 100644 --- a/apps/mobile/src/features/tasks/components/CustomImageBadge.tsx +++ b/apps/mobile/src/features/tasks/components/CustomImageBadge.tsx @@ -1,9 +1,9 @@ import { Text } from "@components/text"; +import type { Task } from "@posthog/shared"; import { Cube } from "phosphor-react-native"; import { View } from "react-native"; import { toRgba } from "@/lib/theme"; import { useCustomImageName } from "../hooks/useCustomImageName"; -import type { Task } from "../types"; // Theme tokens have no violet; a fixed Radix violet-9 mirrors the desktop // custom-image badge and reads well in both light and dark. diff --git a/apps/mobile/src/features/tasks/components/GitHubConnectionPrompt.tsx b/apps/mobile/src/features/tasks/components/GitHubConnectionPrompt.tsx index 296c8ead4e..0da19fca21 100644 --- a/apps/mobile/src/features/tasks/components/GitHubConnectionPrompt.tsx +++ b/apps/mobile/src/features/tasks/components/GitHubConnectionPrompt.tsx @@ -3,8 +3,8 @@ import * as WebBrowser from "expo-web-browser"; import { Pressable, View } from "react-native"; import { useAuthStore } from "@/features/auth"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useThemeColors } from "@/lib/theme"; -import { startGithubUserIntegrationConnect } from "../api"; const log = logger.scope("github-connection-prompt"); @@ -44,7 +44,8 @@ export function GitHubConnectionPrompt({ // and, because we pass `connect_from: "posthog_mobile"`, redirects the // callback to `posthog://github/callback` so this in-app browser closes. try { - const { install_url } = await startGithubUserIntegrationConnect(); + const { install_url } = + await getPostHogApiClient().startGithubUserIntegrationConnect(); authorizeUrl = install_url; } catch (error) { log.error("Failed to start GitHub connection", { error }); diff --git a/apps/mobile/src/features/tasks/components/ScheduleEditor.tsx b/apps/mobile/src/features/tasks/components/ScheduleEditor.tsx index 102e607e87..726973a639 100644 --- a/apps/mobile/src/features/tasks/components/ScheduleEditor.tsx +++ b/apps/mobile/src/features/tasks/components/ScheduleEditor.tsx @@ -1,5 +1,4 @@ import { Text } from "@components/text"; -import { Pressable, TextInput, View } from "react-native"; import { type AutomationScheduleDraft, type AutomationScheduleMode, @@ -7,7 +6,8 @@ import { sanitizeHour, sanitizeMinute, WEEKDAY_OPTIONS, -} from "../utils/automationSchedule"; +} from "@posthog/core/automations/automationSchedule"; +import { Pressable, TextInput, View } from "react-native"; interface ScheduleEditorProps { value: AutomationScheduleDraft; diff --git a/apps/mobile/src/features/tasks/components/SwipeableTaskItem.tsx b/apps/mobile/src/features/tasks/components/SwipeableTaskItem.tsx index a65b4c2576..64bd68eb9a 100644 --- a/apps/mobile/src/features/tasks/components/SwipeableTaskItem.tsx +++ b/apps/mobile/src/features/tasks/components/SwipeableTaskItem.tsx @@ -1,3 +1,5 @@ +import { isTaskRunning } from "@posthog/core/tasks/taskArchive"; +import type { Task } from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { Archive, ArrowCounterClockwise } from "phosphor-react-native"; import { useEffect, useRef } from "react"; @@ -10,11 +12,7 @@ import { View, } from "react-native"; import { useThemeColors } from "@/lib/theme"; -import type { Task } from "../types"; -import { - confirmArchiveRunningTask, - isTaskRunning, -} from "../utils/archiveGuard"; +import { confirmArchiveRunningTask } from "../utils/archiveGuard"; import { TaskItem } from "./TaskItem"; const SWIPE_THRESHOLD = 60; diff --git a/apps/mobile/src/features/tasks/components/TaskItem.test.tsx b/apps/mobile/src/features/tasks/components/TaskItem.test.tsx index d62cfd7665..38cac02b57 100644 --- a/apps/mobile/src/features/tasks/components/TaskItem.test.tsx +++ b/apps/mobile/src/features/tasks/components/TaskItem.test.tsx @@ -1,7 +1,7 @@ +import type { Task } from "@posthog/shared"; import { createElement } from "react"; import { act, create } from "react-test-renderer"; import { describe, expect, it, vi } from "vitest"; -import type { Task } from "../types"; import { TaskItem } from "./TaskItem"; vi.mock("phosphor-react-native", () => ({ diff --git a/apps/mobile/src/features/tasks/components/TaskItem.tsx b/apps/mobile/src/features/tasks/components/TaskItem.tsx index e99bdfb768..c235722b37 100644 --- a/apps/mobile/src/features/tasks/components/TaskItem.tsx +++ b/apps/mobile/src/features/tasks/components/TaskItem.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; +import type { Task } from "@posthog/shared"; import { differenceInHours, format, formatDistanceToNow } from "date-fns"; import { Check, GitPullRequest } from "phosphor-react-native"; import { memo } from "react"; import { Linking, Pressable, View } from "react-native"; import { parseGithubIssueUrl } from "@/lib/githubIssueUrl"; import { useThemeColors } from "@/lib/theme"; -import type { Task } from "../types"; import { TaskStatusIcon } from "./TaskStatusIcon"; function PrBadge({ prUrl, number }: { prUrl: string; number: number }) { diff --git a/apps/mobile/src/features/tasks/components/TaskList.tsx b/apps/mobile/src/features/tasks/components/TaskList.tsx index 403d9c6836..e2a5a7e11a 100644 --- a/apps/mobile/src/features/tasks/components/TaskList.tsx +++ b/apps/mobile/src/features/tasks/components/TaskList.tsx @@ -1,4 +1,6 @@ import { Text } from "@components/text"; +import { taskActivityTimestamp } from "@posthog/core/tasks/taskActivity"; +import type { Task } from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { Archive, GitBranch, Plus, Sparkle, X } from "phosphor-react-native"; import { useCallback, useMemo, useState } from "react"; @@ -13,8 +15,7 @@ import { useThemeColors } from "@/lib/theme"; import { useTasks } from "../hooks/useTasks"; import { useUserIntegrations } from "../hooks/useUserIntegrations"; import { useArchivedTasksStore } from "../stores/archivedTasksStore"; -import { taskActivityTimestamp, useTaskStore } from "../stores/taskStore"; -import type { Task } from "../types"; +import { useTaskStore } from "../stores/taskStore"; import { GitHubConnectionPrompt } from "./GitHubConnectionPrompt"; import { GitHubLoadNotice } from "./GitHubLoadNotice"; import { SwipeableTaskItem } from "./SwipeableTaskItem"; diff --git a/apps/mobile/src/features/tasks/components/TaskStatusIcon.test.ts b/apps/mobile/src/features/tasks/components/TaskStatusIcon.test.ts index 080e0f1ac9..92e99ef8af 100644 --- a/apps/mobile/src/features/tasks/components/TaskStatusIcon.test.ts +++ b/apps/mobile/src/features/tasks/components/TaskStatusIcon.test.ts @@ -1,5 +1,5 @@ +import type { Task } from "@posthog/shared"; import { describe, expect, it } from "vitest"; -import type { Task } from "../types"; import { getTaskStatusIconKind } from "./taskStatusIconKind"; function makeTask(latestRun?: Partial>): Task { @@ -51,25 +51,16 @@ describe("getTaskStatusIconKind", () => { makeTask({ environment: "cloud", status: "queued" }), ), ).toBe("chat"); - expect( getTaskStatusIconKind( makeTask({ environment: "cloud", status: "in_progress" }), ), ).toBe("chat"); - - expect( - getTaskStatusIconKind( - makeTask({ environment: "cloud", status: "started" }), - ), - ).toBe("chat"); - expect( getTaskStatusIconKind( makeTask({ environment: "cloud", status: "completed" }), ), ).toBe("chat"); - expect( getTaskStatusIconKind( makeTask({ environment: "cloud", status: "cancelled" }), @@ -83,7 +74,6 @@ describe("getTaskStatusIconKind", () => { makeTask({ environment: "local", status: "in_progress" }), ), ).toBe("running"); - expect( getTaskStatusIconKind( makeTask({ environment: "local", status: "failed" }), diff --git a/apps/mobile/src/features/tasks/components/TaskStatusIcon.tsx b/apps/mobile/src/features/tasks/components/TaskStatusIcon.tsx index 06c992f047..8736b203b0 100644 --- a/apps/mobile/src/features/tasks/components/TaskStatusIcon.tsx +++ b/apps/mobile/src/features/tasks/components/TaskStatusIcon.tsx @@ -1,3 +1,4 @@ +import type { Task } from "@posthog/shared"; import { ChatCircle, CheckCircle, @@ -9,7 +10,6 @@ import { import { memo, useEffect, useRef } from "react"; import { Animated, Easing } from "react-native"; import { useThemeColors } from "@/lib/theme"; -import type { Task } from "../types"; import { getTaskStatusIconKind } from "./taskStatusIconKind"; interface TaskStatusIconProps { diff --git a/apps/mobile/src/features/tasks/components/taskStatusIconKind.ts b/apps/mobile/src/features/tasks/components/taskStatusIconKind.ts index fa7fbcd357..0fb172132b 100644 --- a/apps/mobile/src/features/tasks/components/taskStatusIconKind.ts +++ b/apps/mobile/src/features/tasks/components/taskStatusIconKind.ts @@ -1,4 +1,4 @@ -import type { Task } from "../types"; +import type { Task } from "@posthog/shared"; export type TaskStatusIconKind = | "pr" @@ -13,7 +13,6 @@ export function getTaskStatusIconKind(task: Task): TaskStatusIconKind { const status = task.latest_run?.status; const environment = task.latest_run?.environment; - // Match desktop semantics, but let PR win when a cloud task also has one. if (prUrl) { return "pr"; } @@ -34,7 +33,7 @@ export function getTaskStatusIconKind(task: Task): TaskStatusIconKind { return "running"; } - if (status === "queued" || status === "started") { + if (status === "queued") { return "started"; } diff --git a/apps/mobile/src/features/tasks/composer/options.test.ts b/apps/mobile/src/features/tasks/composer/options.test.ts new file mode 100644 index 0000000000..75328ebf04 --- /dev/null +++ b/apps/mobile/src/features/tasks/composer/options.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; +import { + DEFAULT_MODEL, + DEFAULT_REASONING, + modelSupportsReasoning, + REASONING_LEVELS, +} from "./options"; + +describe("task composer options", () => { + it("uses an eligible non-premium default model", () => { + expect(DEFAULT_MODEL).toBe("claude-opus-4-8"); + expect(DEFAULT_MODEL).not.toContain("fable"); + }); + + it("derives reasoning defaults and options from shared policy", () => { + expect(DEFAULT_REASONING).toBe("high"); + expect(REASONING_LEVELS.map((option) => option.value)).toEqual([ + "low", + "medium", + "high", + "xhigh", + "max", + ]); + expect(modelSupportsReasoning("claude-opus-4-8")).toBe(true); + expect(modelSupportsReasoning("claude-haiku-4-5")).toBe(false); + }); +}); diff --git a/apps/mobile/src/features/tasks/composer/options.ts b/apps/mobile/src/features/tasks/composer/options.ts index be0b48cdd3..572fff4a35 100644 --- a/apps/mobile/src/features/tasks/composer/options.ts +++ b/apps/mobile/src/features/tasks/composer/options.ts @@ -1,32 +1,39 @@ -export type ExecutionMode = "default" | "acceptEdits" | "plan" | "auto"; -export type ReasoningEffort = "low" | "medium" | "high" | "xhigh" | "max"; +import { + DEFAULT_CLAUDE_EXECUTION_MODE, + getAvailableModes, +} from "@posthog/core/sessions/executionModes"; +import { + DEFAULT_GATEWAY_MODEL, + DEFAULT_REASONING_EFFORT, + defaultEligibleModel, + getReasoningEffortOptions, + type ExecutionMode as SharedExecutionMode, + type SupportedReasoningEffort, +} from "@posthog/shared"; + +export type ExecutionMode = Extract< + SharedExecutionMode, + "default" | "acceptEdits" | "plan" | "auto" +>; +export type ReasoningEffort = SupportedReasoningEffort; export const EXECUTION_MODES: { value: ExecutionMode; label: string; description: string; -}[] = [ - { - value: "plan", - label: "Plan Mode", - description: "Plan first, no tool execution", - }, - { - value: "default", - label: "Default", - description: "Standard behaviour, prompts for dangerous operations", - }, - { - value: "acceptEdits", - label: "Accept Edits", - description: "Auto-accept file edit operations", - }, - { - value: "auto", - label: "Auto", - description: "Model decides which prompts to approve or deny", - }, -]; +}[] = getAvailableModes() + .filter( + (mode): mode is typeof mode & { id: ExecutionMode } => + mode.id === "default" || + mode.id === "acceptEdits" || + mode.id === "plan" || + mode.id === "auto", + ) + .map((mode) => ({ + value: mode.id, + label: mode.name, + description: mode.description, + })); export interface ModelOption { value: string; @@ -68,20 +75,20 @@ export const MODELS: ModelOption[] = [ }, ]; +export const DEFAULT_EXECUTION_MODE: ExecutionMode = + DEFAULT_CLAUDE_EXECUTION_MODE; +export const DEFAULT_MODEL = + defaultEligibleModel(DEFAULT_GATEWAY_MODEL) ?? + MODELS.find((model) => defaultEligibleModel(model.value))?.value ?? + DEFAULT_GATEWAY_MODEL; +export const DEFAULT_REASONING: ReasoningEffort = DEFAULT_REASONING_EFFORT; + export const REASONING_LEVELS: { value: ReasoningEffort; label: string; -}[] = [ - { value: "low", label: "Low" }, - { value: "medium", label: "Medium" }, - { value: "high", label: "High" }, - { value: "xhigh", label: "Extra High" }, - { value: "max", label: "Max" }, -]; - -export const DEFAULT_EXECUTION_MODE: ExecutionMode = "plan"; -export const DEFAULT_MODEL = "claude-opus-4-8"; -export const DEFAULT_REASONING: ReasoningEffort = "high"; +}[] = (getReasoningEffortOptions("claude", DEFAULT_MODEL) ?? []).map( + (option) => ({ value: option.value, label: option.name }), +); export function modelLabel(value: string): string { return MODELS.find((m) => m.value === value)?.label ?? value; @@ -96,5 +103,5 @@ export function reasoningLabel(value: ReasoningEffort): string { } export function modelSupportsReasoning(value: string): boolean { - return MODELS.find((m) => m.value === value)?.supportsReasoning ?? false; + return getReasoningEffortOptions("claude", value) !== null; } diff --git a/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts b/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts index 93c4cf1dc0..772d9a4f69 100644 --- a/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts @@ -8,26 +8,36 @@ const { mockGetTaskAutomations, mockCreateTaskAutomation, mockUpdateTaskAutomation, + mockApiClient, } = vi.hoisted(() => ({ mockUseAuthStore: vi.fn(), mockGetTaskAutomations: vi.fn(), mockCreateTaskAutomation: vi.fn(), mockUpdateTaskAutomation: vi.fn(), + mockApiClient: { + listTaskAutomations: vi.fn(), + getTaskAutomation: vi.fn(), + createTaskAutomation: vi.fn(), + updateTaskAutomation: vi.fn(), + deleteTaskAutomation: vi.fn(), + runTaskAutomation: vi.fn(), + }, })); vi.mock("@/features/auth", () => ({ useAuthStore: mockUseAuthStore, })); -vi.mock("../api", () => ({ - getTaskAutomations: mockGetTaskAutomations, - getTaskAutomation: vi.fn(), - createTaskAutomation: mockCreateTaskAutomation, - updateTaskAutomation: mockUpdateTaskAutomation, - deleteTaskAutomation: vi.fn(), - runTaskAutomation: vi.fn(), +vi.mock("../api", () => ({ runTaskInCloud: vi.fn() })); + +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => mockApiClient, })); +mockApiClient.listTaskAutomations = mockGetTaskAutomations; +mockApiClient.createTaskAutomation = mockCreateTaskAutomation; +mockApiClient.updateTaskAutomation = mockUpdateTaskAutomation; + import { automationKeys, getAutomationPollingInterval, diff --git a/apps/mobile/src/features/tasks/hooks/useAutomations.ts b/apps/mobile/src/features/tasks/hooks/useAutomations.ts index e22d3e6d16..e6db7c11bd 100644 --- a/apps/mobile/src/features/tasks/hooks/useAutomations.ts +++ b/apps/mobile/src/features/tasks/hooks/useAutomations.ts @@ -1,19 +1,12 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { useAuthStore } from "@/features/auth"; -import { logger } from "@/lib/logger"; -import { - createTaskAutomation, - deleteTaskAutomation, - getTaskAutomation, - getTaskAutomations, - runTaskAutomation, - updateTaskAutomation, -} from "../api"; import type { CreateTaskAutomationOptions, TaskAutomation, UpdateTaskAutomationOptions, -} from "../types"; +} from "@posthog/api-client/posthog-client"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useAuthStore } from "@/features/auth"; +import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { taskKeys } from "./useTasks"; const log = logger.scope("automations-mutations"); @@ -59,7 +52,7 @@ export function useAutomations() { const query = useQuery({ queryKey: automationKeys.list(), - queryFn: getTaskAutomations, + queryFn: () => getPostHogApiClient().listTaskAutomations(), enabled: !!projectId && !!oauthAccessToken, refetchInterval: (query) => getAutomationPollingInterval( @@ -80,7 +73,7 @@ export function useAutomation(automationId: string) { return useQuery({ queryKey: automationKeys.detail(automationId), - queryFn: () => getTaskAutomation(automationId), + queryFn: () => getPostHogApiClient().getTaskAutomation(automationId), enabled: !!projectId && !!oauthAccessToken && !!automationId, refetchInterval: (query) => getAutomationPollingInterval( @@ -94,7 +87,7 @@ export function useCreateTaskAutomation() { return useMutation({ mutationFn: (options: CreateTaskAutomationOptions) => - createTaskAutomation(options), + getPostHogApiClient().createTaskAutomation(options), onSuccess: (automation) => { queryClient.setQueryData( automationKeys.detail(automation.id), @@ -118,7 +111,7 @@ export function useUpdateTaskAutomation() { }: { automationId: string; updates: UpdateTaskAutomationOptions; - }) => updateTaskAutomation(automationId, updates), + }) => getPostHogApiClient().updateTaskAutomation(automationId, updates), onSuccess: (automation, { automationId }) => { queryClient.setQueryData( automationKeys.detail(automationId), @@ -136,7 +129,8 @@ export function useDeleteTaskAutomation() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (automationId: string) => deleteTaskAutomation(automationId), + mutationFn: (automationId: string) => + getPostHogApiClient().deleteTaskAutomation(automationId), onSuccess: (_, automationId) => { queryClient.removeQueries({ queryKey: automationKeys.detail(automationId), @@ -153,7 +147,8 @@ export function useRunTaskAutomation() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (automationId: string) => runTaskAutomation(automationId), + mutationFn: (automationId: string) => + getPostHogApiClient().runTaskAutomation(automationId), onSuccess: (automation, automationId) => { queryClient.setQueryData( automationKeys.detail(automationId), diff --git a/apps/mobile/src/features/tasks/hooks/useCustomImageName.ts b/apps/mobile/src/features/tasks/hooks/useCustomImageName.ts index a634ecd5e6..a8bf916ce8 100644 --- a/apps/mobile/src/features/tasks/hooks/useCustomImageName.ts +++ b/apps/mobile/src/features/tasks/hooks/useCustomImageName.ts @@ -1,6 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { useAuthStore } from "@/features/auth"; -import { getSandboxCustomImages, getSandboxEnvironments } from "../api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; export const sandboxKeys = { customImages: () => ["sandbox-custom-images"] as const, @@ -26,7 +26,7 @@ export function useCustomImageName({ const imagesQuery = useQuery({ queryKey: sandboxKeys.customImages(), - queryFn: getSandboxCustomImages, + queryFn: () => getPostHogApiClient().listSandboxCustomImages(), enabled: canQuery && hasImageRef, staleTime: 60_000, retry: 0, @@ -34,7 +34,7 @@ export function useCustomImageName({ const environmentsQuery = useQuery({ queryKey: sandboxKeys.environments(), - queryFn: getSandboxEnvironments, + queryFn: () => getPostHogApiClient().listSandboxEnvironments(), enabled: canQuery && !!sandboxEnvironmentId, staleTime: 60_000, retry: 0, diff --git a/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts b/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts index 070fb37c47..45040a2df6 100644 --- a/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts @@ -14,9 +14,11 @@ vi.mock("@/features/auth", () => ({ useAuthStore: mockUseAuthStore, })); -vi.mock("../api", () => ({ - getGithubRepositories: mockGetGithubRepositories, - getIntegrations: mockGetIntegrations, +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ + getGithubRepositories: mockGetGithubRepositories, + getIntegrations: mockGetIntegrations, + }), })); import { useRepositoryCacheStore } from "../stores/repositoryCacheStore"; diff --git a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts index 49c1638780..bf2aab3c77 100644 --- a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts @@ -1,9 +1,9 @@ import { useQuery } from "@tanstack/react-query"; import { useEffect, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; -import { getGithubRepositories, getIntegrations } from "../api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useRepositoryCacheStore } from "../stores/repositoryCacheStore"; -import type { RepositoryOption } from "../types"; +import type { Integration, RepositoryOption } from "../types"; import { buildRepositoryOptions } from "../utils/repositorySelection"; /** Cheap content-equality check for repository option lists. Lets the cache @@ -59,8 +59,27 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { const integrationsQuery = useQuery({ queryKey: integrationKeys.github(), queryFn: async () => { - const data = await getIntegrations(); - return data.filter((i) => i.kind === "github"); + const data = await getPostHogApiClient().getIntegrations(); + return data.flatMap((integration): Integration[] => { + if ( + integration.kind !== "github" || + typeof integration.id !== "number" + ) { + return []; + } + + return [ + { + id: integration.id, + kind: integration.kind, + display_name: + typeof integration.display_name === "string" + ? integration.display_name + : undefined, + config: integration.config as Integration["config"], + }, + ]; + }); }, enabled: enabled && !!projectId && !!oauthAccessToken, }); @@ -78,7 +97,9 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { const results = await Promise.allSettled( githubIntegrations.map(async (integration) => ({ integrationId: integration.id, - repositories: await getGithubRepositories(integration.id), + repositories: await getPostHogApiClient().getGithubRepositories( + integration.id, + ), })), ); diff --git a/apps/mobile/src/features/tasks/hooks/useTasks.test.ts b/apps/mobile/src/features/tasks/hooks/useTasks.test.ts index 6cd5c33ada..579ec4bce2 100644 --- a/apps/mobile/src/features/tasks/hooks/useTasks.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useTasks.test.ts @@ -28,12 +28,17 @@ vi.mock("@/lib/logger", () => { }); vi.mock("../api", () => ({ - createTask: vi.fn(), - deleteTask: vi.fn(), - getTask: vi.fn(), - getTasks: vi.fn(), runTaskInCloud: vi.fn(), - updateTask: vi.fn(), +})); + +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ + createTask: vi.fn(), + deleteTask: vi.fn(), + getTask: vi.fn(), + getTasks: vi.fn(), + updateTask: vi.fn(), + }), })); vi.mock("../stores/taskStore", () => ({ diff --git a/apps/mobile/src/features/tasks/hooks/useTasks.ts b/apps/mobile/src/features/tasks/hooks/useTasks.ts index 1af7d5aa84..95b5d4725f 100644 --- a/apps/mobile/src/features/tasks/hooks/useTasks.ts +++ b/apps/mobile/src/features/tasks/hooks/useTasks.ts @@ -1,16 +1,12 @@ +import { filterAndSortTasks } from "@posthog/core/tasks/taskActivity"; +import type { Task } from "@posthog/shared"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useAuthStore, useUserQuery } from "@/features/auth"; import { logger } from "@/lib/logger"; -import { - createTask, - deleteTask, - getTask, - getTasks, - runTaskInCloud, - updateTask, -} from "../api"; -import { filterAndSortTasks, useTaskStore } from "../stores/taskStore"; -import type { CreateTaskOptions, Task } from "../types"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; +import { runTaskInCloud } from "../api"; +import { useTaskStore } from "../stores/taskStore"; +import type { CreateTaskOptions } from "../types"; const log = logger.scope("tasks-mutations"); const ACTIVE_TASK_POLLING_INTERVAL_MS = 5_000; @@ -69,7 +65,7 @@ export function useTasks(filters?: { const query = useQuery({ queryKey: taskKeys.list(queryFilters), - queryFn: () => getTasks(queryFilters), + queryFn: () => getPostHogApiClient().getTasks(queryFilters), enabled: !!projectId && !!oauthAccessToken && !!currentUser?.id, refetchInterval: (query) => getTaskPollingInterval(query.state.data as Task[] | undefined), @@ -102,7 +98,7 @@ export function useTask(taskId: string) { return useQuery({ queryKey: taskKeys.detail(taskId), - queryFn: () => getTask(taskId), + queryFn: () => getPostHogApiClient().getTask(taskId), enabled: !!projectId && !!oauthAccessToken && !!taskId, refetchInterval: (query) => getTaskPollingInterval(query.state.data as Task | undefined), @@ -117,7 +113,8 @@ export function useCreateTask() { }; const mutation = useMutation({ - mutationFn: (options: CreateTaskOptions) => createTask(options), + mutationFn: (options: CreateTaskOptions) => + getPostHogApiClient().createTask(options), onSuccess: () => { invalidateTasks(); }, @@ -139,7 +136,13 @@ export function useUpdateTask() { }: { taskId: string; updates: Partial; - }) => updateTask(taskId, updates), + }) => + getPostHogApiClient().updateTask( + taskId, + updates as Parameters< + ReturnType["updateTask"] + >[1], + ), onSuccess: (updatedTask, { taskId }) => { // Update the detail cache immediately queryClient.setQueryData(taskKeys.detail(taskId), updatedTask); @@ -155,7 +158,7 @@ export function useDeleteTask() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (taskId: string) => deleteTask(taskId), + mutationFn: (taskId: string) => getPostHogApiClient().deleteTask(taskId), onSuccess: (_, taskId) => { // Remove from detail cache queryClient.removeQueries({ queryKey: taskKeys.detail(taskId) }); diff --git a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts index 68ae4f9808..1ba6655cf2 100644 --- a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts @@ -1,8 +1,8 @@ import { useQuery } from "@tanstack/react-query"; import { useCallback, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; -import { getUserGithubIntegrations, getUserGithubRepositories } from "../api"; -import type { RepositoryOption, UserGithubIntegration } from "../types"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; +import type { RepositoryOption } from "../types"; /** * User-scoped sibling of {@link useIntegrations}. Reads the authenticated @@ -29,7 +29,10 @@ interface UseUserIntegrationsOptions { enabled?: boolean; } -function integrationLabel(integration: UserGithubIntegration): string { +function integrationLabel(integration: { + installation_id: string; + account?: { name?: string | null } | null; +}): string { return integration.account?.name ?? `GitHub ${integration.installation_id}`; } @@ -39,7 +42,7 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { const integrationsQuery = useQuery({ queryKey: userIntegrationKeys.github(), - queryFn: getUserGithubIntegrations, + queryFn: () => getPostHogApiClient().getGithubUserIntegrations(), enabled: enabled && !!oauthAccessToken, }); @@ -54,7 +57,7 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { const results = await Promise.allSettled( integrations.map(async (integration) => ({ installationId: integration.installation_id, - repositories: await getUserGithubRepositories( + repositories: await getPostHogApiClient().getGithubUserRepositories( integration.installation_id, ), })), diff --git a/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx b/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx index 5b757d40fc..175a77c276 100644 --- a/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx +++ b/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx @@ -8,8 +8,8 @@ const flagState = vi.hoisted(() => ({ enabled: true as boolean })); vi.mock("posthog-react-native", () => ({ useFeatureFlag: () => flagState.enabled, })); -vi.mock("@/features/tasks/api", () => ({ - warmTask: mockWarmTask, +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ warmTask: mockWarmTask }), })); vi.mock("@/lib/logger", () => { const mockLogger = { diff --git a/apps/mobile/src/features/tasks/hooks/useWarmTask.ts b/apps/mobile/src/features/tasks/hooks/useWarmTask.ts index 935619f75f..d3034f8638 100644 --- a/apps/mobile/src/features/tasks/hooks/useWarmTask.ts +++ b/apps/mobile/src/features/tasks/hooks/useWarmTask.ts @@ -1,8 +1,8 @@ import { TASKS_PREWARM_SANDBOX_FLAG } from "@posthog/shared"; import { useFeatureFlag } from "posthog-react-native"; import { useEffect, useRef } from "react"; -import { warmTask } from "@/features/tasks/api"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; const log = logger.scope("warm-task"); @@ -79,21 +79,23 @@ export function useWarmTask({ debounceRef.current = setTimeout(() => { debounceRef.current = null; lastWarmedKeyRef.current = key; - void warmTask({ - repository: repo, - github_integration: githubIntegration, - branch: warmBranch, - runtime_adapter: warmRuntimeAdapter, - model: warmModel, - reasoning_effort: warmReasoningEffort, - ...(warmSandboxEnvironmentId - ? { sandbox_environment_id: warmSandboxEnvironmentId } - : {}), - ...(warmCustomImageId ? { custom_image_id: warmCustomImageId } : {}), - }).catch((error) => { - lastWarmedKeyRef.current = null; - log.warn("Failed to warm task", error); - }); + void getPostHogApiClient() + .warmTask({ + repository: repo, + github_integration: githubIntegration, + branch: warmBranch, + runtime_adapter: warmRuntimeAdapter, + model: warmModel, + reasoning_effort: warmReasoningEffort, + ...(warmSandboxEnvironmentId + ? { sandbox_environment_id: warmSandboxEnvironmentId } + : {}), + ...(warmCustomImageId ? { custom_image_id: warmCustomImageId } : {}), + }) + .catch((error) => { + lastWarmedKeyRef.current = null; + log.warn("Failed to warm task", error); + }); }, WARM_DEBOUNCE_MS); return clearDebounce; diff --git a/apps/mobile/src/features/tasks/index.ts b/apps/mobile/src/features/tasks/index.ts index 07c05346e7..7da4db747e 100644 --- a/apps/mobile/src/features/tasks/index.ts +++ b/apps/mobile/src/features/tasks/index.ts @@ -1,7 +1,5 @@ // Tasks feature -// API -export * from "./api"; // Components export { TaskItem } from "./components/TaskItem"; export { TaskList } from "./components/TaskList"; @@ -29,7 +27,6 @@ export * from "./types"; // Utils export { - convertRawEntriesToEvents, convertStoredEntriesToEvents, parseSessionLogs, } from "./utils/parseSessionLogs"; diff --git a/apps/mobile/src/features/tasks/lib/cloudTaskStream.test.ts b/apps/mobile/src/features/tasks/lib/cloudTaskStream.test.ts new file mode 100644 index 0000000000..a3fc53a610 --- /dev/null +++ b/apps/mobile/src/features/tasks/lib/cloudTaskStream.test.ts @@ -0,0 +1,54 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + engine: { + off: vi.fn(), + on: vi.fn(), + reconnectIfDisconnected: vi.fn(), + unwatch: vi.fn(), + watch: vi.fn(), + }, +})); + +vi.mock("@posthog/core/cloud-task/cloud-task-engine", () => ({ + createCloudTaskEngine: () => mocks.engine, +})); + +vi.mock("@posthog/core/cloud-task/schemas", () => ({ + CloudTaskEvent: { Update: "cloud-task-update" }, +})); + +vi.mock("expo/fetch", () => ({ fetch: vi.fn() })); + +vi.mock("@/lib/api", () => ({ + authedFetch: vi.fn(), + getBaseUrl: () => "https://app.posthog.test", + getProjectId: () => 42, +})); + +vi.mock("@/lib/logger", () => ({ + logger: { scope: vi.fn() }, +})); + +import { watchCloudTask } from "./cloudTaskStream"; + +describe("watchCloudTask", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("asks the shared engine to reconnect only when disconnected", () => { + const handle = watchCloudTask({ + taskId: "task-1", + runId: "run-1", + onUpdate: vi.fn(), + }); + + handle.reconnectIfDisconnected(); + + expect(mocks.engine.reconnectIfDisconnected).toHaveBeenCalledWith( + "task-1", + "run-1", + ); + }); +}); diff --git a/apps/mobile/src/features/tasks/lib/cloudTaskStream.ts b/apps/mobile/src/features/tasks/lib/cloudTaskStream.ts index 2541eed7e7..ac761b6bc9 100644 --- a/apps/mobile/src/features/tasks/lib/cloudTaskStream.ts +++ b/apps/mobile/src/features/tasks/lib/cloudTaskStream.ts @@ -1,120 +1,18 @@ -import { fetch } from "expo/fetch"; -import { createTimeoutSignal } from "@/lib/api"; -import { logger } from "@/lib/logger"; import { - fetchSessionLogs, - getTaskRun, - HttpError, - streamCloudTask, -} from "../api"; + type CloudTaskEngine, + type CloudTaskFetch, + createCloudTaskEngine, +} from "@posthog/core/cloud-task/cloud-task-engine"; +import { CloudTaskEvent } from "@posthog/core/cloud-task/schemas"; +import type { CloudTaskUpdatePayload } from "@posthog/shared"; +import { fetch } from "expo/fetch"; import { - type CloudTaskUpdatePayload, - isKeepaliveEvent, - isPermissionRequestEvent, - isSseErrorEvent, - isTaskRunStateEvent, - isTerminalStatus, - type StoredLogEntry, - type TaskRun, - type TaskRunStateEvent, - type TaskRunStatus, -} from "../types"; -import { parseSessionLogs } from "../utils/parseSessionLogs"; -import { type SseEvent, SseEventParser } from "./sseParser"; - -const log = logger.scope("cloud-task-stream"); - -const MAX_SSE_RECONNECT_ATTEMPTS = 5; -const SSE_RECONNECT_BASE_DELAY_MS = 2_000; -const SSE_RECONNECT_MAX_DELAY_MS = 30_000; -const EVENT_BATCH_FLUSH_MS = 16; -const EVENT_BATCH_MAX_SIZE = 50; -const SESSION_LOG_PAGE_LIMIT = 5_000; - -interface CloudTaskConnectionError { - title: string; - message: string; - retryable: boolean; - autoRetry?: boolean; -} - -class CloudTaskStreamError extends Error { - constructor( - message: string, - public readonly details: CloudTaskConnectionError, - public readonly status?: number, - ) { - super(message); - this.name = "CloudTaskStreamError"; - } -} - -function createStreamStatusError(status: number): CloudTaskStreamError { - switch (status) { - case 401: - return new CloudTaskStreamError( - "Cloud authentication expired", - { - title: "Cloud authentication expired", - message: "Please reauthenticate and retry the cloud run stream.", - retryable: true, - autoRetry: false, - }, - status, - ); - case 403: - return new CloudTaskStreamError( - "Cloud access denied", - { - title: "Cloud access denied", - message: - "You no longer have access to this cloud run. Reauthenticate and retry.", - retryable: true, - autoRetry: false, - }, - status, - ); - case 404: - return new CloudTaskStreamError( - "Cloud run not found", - { - title: "Cloud run not found", - message: - "This cloud run could not be found. It may have been deleted or moved.", - retryable: false, - autoRetry: false, - }, - status, - ); - case 406: - return new CloudTaskStreamError( - "Cloud stream unavailable", - { - title: "Cloud stream unavailable", - message: - "The backend rejected the live stream request. Restart the backend and retry.", - retryable: true, - autoRetry: false, - }, - status, - ); - default: - return new CloudTaskStreamError( - `Stream request failed with status ${status}`, - { - title: "Cloud stream failed", - message: `The cloud stream request failed with status ${status}. Retry to reconnect.`, - retryable: true, - autoRetry: true, - }, - status, - ); - } -} - -function shouldFailWatcherForFetchStatus(status: number): boolean { - return status === 401 || status === 403 || status === 404; -} + authedFetch, + type FetchInit, + getBaseUrl, + getProjectId, +} from "@/lib/api"; +import { logger } from "@/lib/logger"; export interface WatchCloudTaskOptions { taskId: string; @@ -127,786 +25,78 @@ export interface WatchCloudTaskHandle { reconnectIfDisconnected: () => void; } -interface WatcherState { - taskId: string; - runId: string; - onUpdate: (update: CloudTaskUpdatePayload) => void; - stopped: boolean; - sseAbortController: AbortController | null; - reconnectTimeoutId: ReturnType | null; - batchFlushTimeoutId: ReturnType | null; - pendingLogEntries: StoredLogEntry[]; - totalEntryCount: number; - reconnectAttempts: number; - lastEventId: string | null; - lastStatus: TaskRunStatus | null; - lastStage: string | null; - lastOutput: Record | null; - lastErrorMessage: string | null; - lastBranch: string | null; - lastStatusUpdatedAt: string | null; - isBootstrapping: boolean; - hasEmittedSnapshot: boolean; - bufferedLogBatches: StoredLogEntry[][]; - failed: boolean; - needsPostBootstrapReconnect: boolean; - needsStopAfterBootstrap: boolean; -} - -export function watchCloudTask( - options: WatchCloudTaskOptions, -): WatchCloudTaskHandle { - const watcher: WatcherState = { - taskId: options.taskId, - runId: options.runId, - onUpdate: options.onUpdate, - stopped: false, - sseAbortController: null, - reconnectTimeoutId: null, - batchFlushTimeoutId: null, - pendingLogEntries: [], - totalEntryCount: 0, - reconnectAttempts: 0, - lastEventId: null, - lastStatus: null, - lastStage: null, - lastOutput: null, - lastErrorMessage: null, - lastBranch: null, - lastStatusUpdatedAt: null, - isBootstrapping: false, - hasEmittedSnapshot: false, - bufferedLogBatches: [], - failed: false, - needsPostBootstrapReconnect: false, - needsStopAfterBootstrap: false, - }; - - void bootstrapWatcher(watcher); - - return { - stop: () => stopWatcher(watcher), - reconnectIfDisconnected: () => { - if ( - watcher.stopped || - watcher.failed || - isTerminalStatus(watcher.lastStatus) - ) { - return; - } - if (watcher.sseAbortController || watcher.reconnectTimeoutId) { - return; - } - log.debug("Force reconnect after suspension", { runId: watcher.runId }); - watcher.reconnectAttempts = 0; - void connectSse(watcher, { - startLatest: !watcher.lastEventId, - }); +const mobileCloudTaskAnalytics = { + initialize: () => {}, + track: () => {}, + identify: () => {}, + setCurrentUserId: () => {}, + getCurrentUserId: () => null, + getOrCreateSessionId: () => "mobile-cloud-task", + resetUser: () => {}, + captureException: () => {}, + flush: async () => {}, + shutdown: async () => {}, +}; + +let cloudTaskEngine: CloudTaskEngine | null = null; + +function getCloudTaskEngine(): CloudTaskEngine { + if (cloudTaskEngine) { + return cloudTaskEngine; + } + + cloudTaskEngine = createCloudTaskEngine({ + auth: { + authenticatedFetch: (url, init) => + authedFetch(url, init as FetchInit | undefined), + getCloudContext: async () => ({ + apiHost: getBaseUrl(), + teamId: getProjectId(), + }), }, - }; -} - -function stopWatcher(watcher: WatcherState): void { - if (watcher.stopped) return; - watcher.stopped = true; - - watcher.sseAbortController?.abort(); - watcher.sseAbortController = null; - - if (watcher.reconnectTimeoutId) { - clearTimeout(watcher.reconnectTimeoutId); - watcher.reconnectTimeoutId = null; - } - - if (watcher.batchFlushTimeoutId) { - clearTimeout(watcher.batchFlushTimeoutId); - watcher.batchFlushTimeoutId = null; - } - - // Drop any unflushed batches; the consumer is gone. - watcher.pendingLogEntries = []; - watcher.bufferedLogBatches = []; -} - -async function bootstrapWatcher(watcher: WatcherState): Promise { - if (watcher.stopped) return; - - watcher.failed = false; - watcher.needsPostBootstrapReconnect = false; - watcher.needsStopAfterBootstrap = false; - - const run = await fetchTaskRunState(watcher); - if (watcher.stopped || watcher.failed) return; - - if (!run) { - failWatcher(watcher, { - title: "Failed to load cloud run", - message: "Could not fetch the cloud run state. Retry to reconnect.", - retryable: true, - }); - return; - } - - applyTaskRunState(watcher, run); - - if (isTerminalStatus(run.status)) { - const historicalEntries = await fetchHistoricalEntries(watcher, run); - if (watcher.stopped || watcher.failed) return; - if (!historicalEntries) { - failWatcher(watcher, { - title: "Failed to load task history", - message: - "Could not load the persisted cloud task logs. Retry to reconnect.", - retryable: true, - }); - return; - } - - watcher.totalEntryCount = historicalEntries.length; - watcher.hasEmittedSnapshot = true; - emitSnapshot(watcher, historicalEntries); - stopWatcher(watcher); - return; - } - - watcher.isBootstrapping = true; - watcher.bufferedLogBatches = []; - void connectSse(watcher, { startLatest: true }); - - const historicalEntries = await fetchHistoricalEntries(watcher, run); - if (watcher.stopped || watcher.failed) return; - if (!historicalEntries) { - failWatcher(watcher, { - title: "Failed to load cloud run history", - message: - "Could not load the existing cloud run logs. Retry to reconnect.", - retryable: true, - }); - return; - } - - // Flush any pending live entries into the bootstrap buffer before snapshot. - flushLogBatch(watcher); - - watcher.totalEntryCount = historicalEntries.length; - watcher.hasEmittedSnapshot = true; - emitSnapshot(watcher, historicalEntries); - - watcher.isBootstrapping = false; - drainBufferedLogBatches(watcher, historicalEntries); - - if (watcher.failed) return; - - if (watcher.needsStopAfterBootstrap || isTerminalStatus(watcher.lastStatus)) { - watcher.needsStopAfterBootstrap = false; - stopWatcher(watcher); - return; - } - - if (watcher.needsPostBootstrapReconnect) { - watcher.needsPostBootstrapReconnect = false; - scheduleReconnect(watcher, undefined, { countAttempt: false }); - } - - void verifyPostBootstrapStatus(watcher); -} - -async function verifyPostBootstrapStatus(watcher: WatcherState): Promise { - if (watcher.stopped) return; - if (isTerminalStatus(watcher.lastStatus)) return; - - const run = await fetchTaskRunState(watcher); - if (watcher.stopped || !run) return; - - if (!applyTaskRunState(watcher, run)) return; - if (isTerminalStatus(watcher.lastStatus)) return; - - emitStatus(watcher); -} - -async function connectSse( - watcher: WatcherState, - options?: { startLatest?: boolean }, -): Promise { - if (watcher.stopped) return; - - const controller = new AbortController(); - watcher.sseAbortController = controller; - - const parser = new SseEventParser(); - const decoder = new TextDecoder(); - - try { - const response = await streamCloudTask(watcher.taskId, watcher.runId, { - lastEventId: watcher.lastEventId, - startLatest: options?.startLatest, - signal: controller.signal, - }); - - if (!response.ok) { - throw createStreamStatusError(response.status); - } - - if (!response.body) { - throw new Error("Stream response did not include a body"); - } - - const reader = response.body.getReader(); - - while (true) { - const { done, value } = await reader.read(); - if (done) { - break; - } - - if (!value) { - continue; - } - - const chunk = decoder.decode(value, { stream: true }); - const events = parser.parse(chunk); - for (const event of events) { - handleSseEvent(watcher, event); - if (watcher.failed) return; - } - } - - const trailingEvents = parser.parse(decoder.decode()); - for (const event of trailingEvents) { - handleSseEvent(watcher, event); - if (watcher.failed) return; - } - - flushLogBatch(watcher); - - if (controller.signal.aborted) { - return; - } - - await handleStreamCompletion(watcher, { reconnectIfNonTerminal: true }); - } catch (error) { - flushLogBatch(watcher); - - if (controller.signal.aborted) { - return; - } - - if ( - error instanceof CloudTaskStreamError && - error.details.autoRetry === false - ) { - failWatcher(watcher, error.details); - return; - } - - const errorMessage = - error instanceof Error ? error.message : "Unknown stream error"; - log.warn("Cloud task stream error", { - runId: watcher.runId, - error: errorMessage, - }); - await handleStreamCompletion(watcher, { - reconnectIfNonTerminal: true, - reconnectError: error, - countReconnectAttempt: true, - }); - } finally { - if (watcher.sseAbortController === controller) { - watcher.sseAbortController = null; - } - } -} - -function handleSseEvent(watcher: WatcherState, event: SseEvent): void { - if (watcher.failed || watcher.stopped) return; - - if (event.id) { - watcher.lastEventId = event.id; - } - - if (event.event === "error") { - const message = isSseErrorEvent(event.data) - ? event.data.error - : "Unknown stream error"; - throw new Error(message); - } - - if (event.event === "keepalive" || isKeepaliveEvent(event.data)) { - return; - } - - watcher.reconnectAttempts = 0; - - if (isTaskRunStateEvent(event.data)) { - if (applyTaskRunState(watcher, event.data)) { - if (!watcher.isBootstrapping && !isTerminalStatus(watcher.lastStatus)) { - emitStatus(watcher); - } - } - return; - } - - if (isPermissionRequestEvent(event.data)) { - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "permission_request", - requestId: event.data.requestId, - toolCall: event.data.toolCall, - options: event.data.options, - }); - return; - } - - // StoredLogEntry always has a string `type`. Anything else is a server - // event the mobile client doesn't understand yet — drop it instead of - // forwarding a malformed entry to convertStoredEntriesToEvents. - if ( - typeof event.data !== "object" || - event.data === null || - typeof (event.data as { type?: unknown }).type !== "string" - ) { - log.warn("Skipping unrecognized SSE event", { - runId: watcher.runId, - eventName: event.event, - }); - return; - } - - watcher.pendingLogEntries.push(event.data as StoredLogEntry); - if (watcher.pendingLogEntries.length >= EVENT_BATCH_MAX_SIZE) { - flushLogBatch(watcher); - return; - } - - if (!watcher.batchFlushTimeoutId) { - watcher.batchFlushTimeoutId = setTimeout(() => { - watcher.batchFlushTimeoutId = null; - flushLogBatch(watcher); - }, EVENT_BATCH_FLUSH_MS); - } -} - -function flushLogBatch(watcher: WatcherState): void { - if (watcher.pendingLogEntries.length === 0) return; - - if (watcher.batchFlushTimeoutId) { - clearTimeout(watcher.batchFlushTimeoutId); - watcher.batchFlushTimeoutId = null; - } - - const entries = watcher.pendingLogEntries; - watcher.pendingLogEntries = []; - - if (watcher.isBootstrapping) { - watcher.bufferedLogBatches.push(entries); - return; - } - - watcher.totalEntryCount += entries.length; - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "logs", - newEntries: entries, - totalEntryCount: watcher.totalEntryCount, + analytics: mobileCloudTaskAnalytics, + logger, + streamFetch: fetch as CloudTaskFetch, }); -} - -function drainBufferedLogBatches( - watcher: WatcherState, - historicalEntries: StoredLogEntry[], -): void { - if (watcher.bufferedLogBatches.length === 0) return; - - // Content-based dedup because SSE IDs (Redis stream IDs) don't exist in - // the S3-backed historical entries — the JSON payload is the only shared key. - const historicalCounts = new Map(); - for (const entry of historicalEntries) { - const serialized = JSON.stringify(entry); - historicalCounts.set( - serialized, - (historicalCounts.get(serialized) ?? 0) + 1, - ); - } - - for (const entries of watcher.bufferedLogBatches) { - const dedupedEntries = entries.filter((entry) => { - const serialized = JSON.stringify(entry); - const remaining = historicalCounts.get(serialized) ?? 0; - if (remaining <= 0) return true; - historicalCounts.set(serialized, remaining - 1); - return false; - }); - if (dedupedEntries.length === 0) continue; - - watcher.totalEntryCount += dedupedEntries.length; - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "logs", - newEntries: dedupedEntries, - totalEntryCount: watcher.totalEntryCount, - }); - } - - watcher.bufferedLogBatches = []; -} - -function emitSnapshot(watcher: WatcherState, entries: StoredLogEntry[]): void { - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "snapshot", - newEntries: entries, - totalEntryCount: watcher.totalEntryCount, - status: watcher.lastStatus ?? undefined, - stage: watcher.lastStage, - output: watcher.lastOutput, - errorMessage: watcher.lastErrorMessage, - branch: watcher.lastBranch, - }); -} - -function emitStatus(watcher: WatcherState): void { - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "status", - status: watcher.lastStatus ?? undefined, - stage: watcher.lastStage, - output: watcher.lastOutput, - errorMessage: watcher.lastErrorMessage, - branch: watcher.lastBranch, - }); -} - -function failWatcher( - watcher: WatcherState, - error: CloudTaskConnectionError, -): void { - if (watcher.stopped) return; - - watcher.failed = true; - watcher.isBootstrapping = false; - watcher.pendingLogEntries = []; - watcher.bufferedLogBatches = []; - - if (watcher.reconnectTimeoutId) { - clearTimeout(watcher.reconnectTimeoutId); - watcher.reconnectTimeoutId = null; - } - - if (watcher.batchFlushTimeoutId) { - clearTimeout(watcher.batchFlushTimeoutId); - watcher.batchFlushTimeoutId = null; - } - - watcher.sseAbortController?.abort(); - watcher.sseAbortController = null; - - watcher.onUpdate({ - taskId: watcher.taskId, - runId: watcher.runId, - kind: "error", - errorTitle: error.title, - errorMessage: error.message, - retryable: error.retryable, - }); + return cloudTaskEngine; } -function scheduleReconnect( - watcher: WatcherState, - error?: unknown, - options: { countAttempt?: boolean } = {}, -): void { - if ( - watcher.stopped || - watcher.failed || - isTerminalStatus(watcher.lastStatus) - ) { - return; - } - - if (watcher.reconnectTimeoutId) { - clearTimeout(watcher.reconnectTimeoutId); - } - - const countAttempt = options.countAttempt ?? true; - if (countAttempt) { - watcher.reconnectAttempts += 1; - } else { - watcher.reconnectAttempts = 0; - } - - if (watcher.reconnectAttempts > MAX_SSE_RECONNECT_ATTEMPTS) { - const details = - error instanceof CloudTaskStreamError - ? error.details - : { - title: "Cloud stream disconnected", - message: - "Lost connection to the cloud run stream. Retry to reconnect.", - retryable: true, - }; - failWatcher(watcher, details); - return; - } - - const delay = Math.min( - SSE_RECONNECT_BASE_DELAY_MS * - 2 ** Math.max(watcher.reconnectAttempts - 1, 0), - SSE_RECONNECT_MAX_DELAY_MS, - ); - - watcher.reconnectTimeoutId = setTimeout(() => { - if (watcher.stopped) return; - watcher.reconnectTimeoutId = null; - void connectSse(watcher, { - startLatest: watcher.isBootstrapping || watcher.hasEmittedSnapshot, - }); - }, delay); -} - -async function handleStreamCompletion( - watcher: WatcherState, - options: { - reconnectIfNonTerminal: boolean; - reconnectError?: unknown; - countReconnectAttempt?: boolean; - }, -): Promise { - if (watcher.stopped) return; - - const { reconnectIfNonTerminal } = options; - const run = await fetchTaskRunState(watcher); - if (watcher.stopped || watcher.failed) return; - - if (watcher.isBootstrapping) { - if (!run) { - watcher.needsPostBootstrapReconnect = true; - return; - } - - applyTaskRunState(watcher, run); - if (isTerminalStatus(watcher.lastStatus) || !reconnectIfNonTerminal) { - watcher.needsStopAfterBootstrap = true; - } else { - watcher.needsPostBootstrapReconnect = true; - } - return; - } - - if (!run) { - scheduleReconnect( - watcher, - new CloudTaskStreamError("Failed to fetch terminal cloud run state", { - title: "Cloud run state unavailable", - message: - "Could not fetch the latest cloud run state after the stream ended. Retry to reconnect.", - retryable: true, - }), - ); - return; - } - - const stateChanged = applyTaskRunState(watcher, run); - - if (!isTerminalStatus(watcher.lastStatus) && reconnectIfNonTerminal) { - if (stateChanged) { - emitStatus(watcher); - } - log.warn("Cloud task stream ended before terminal status", { - runId: watcher.runId, - status: watcher.lastStatus, - }); - scheduleReconnect(watcher, options.reconnectError, { - countAttempt: options.countReconnectAttempt ?? false, - }); - return; - } - - emitStatus(watcher); - stopWatcher(watcher); -} - -function applyTaskRunState( - watcher: WatcherState, - run: - | Pick< - TaskRun, - | "status" - | "stage" - | "output" - | "error_message" - | "branch" - | "updated_at" - > - | TaskRunStateEvent, -): boolean { - const updatedAt = run.updated_at ?? null; - if ( - updatedAt && - watcher.lastStatusUpdatedAt && - Date.parse(updatedAt) <= Date.parse(watcher.lastStatusUpdatedAt) - ) { - return false; - } - - const nextStatus = run.status ?? watcher.lastStatus; - const nextStage = run.stage ?? null; - const nextOutput = run.output ?? null; - const nextErrorMessage = run.error_message ?? null; - const nextBranch = run.branch ?? null; - - const changed = - nextStatus !== watcher.lastStatus || - nextStage !== watcher.lastStage || - JSON.stringify(nextOutput) !== JSON.stringify(watcher.lastOutput) || - nextErrorMessage !== watcher.lastErrorMessage || - nextBranch !== watcher.lastBranch; - - watcher.lastStatus = nextStatus ?? null; - watcher.lastStage = nextStage; - watcher.lastOutput = nextOutput; - watcher.lastErrorMessage = nextErrorMessage; - watcher.lastBranch = nextBranch; - if (updatedAt) { - watcher.lastStatusUpdatedAt = updatedAt; - } - - return changed; -} - -async function fetchTaskRunState( - watcher: WatcherState, -): Promise { - try { - return await getTaskRun(watcher.taskId, watcher.runId); - } catch (error) { - if (error instanceof HttpError) { - log.warn("Cloud task status fetch failed", { - runId: watcher.runId, - status: error.status, - }); - if (shouldFailWatcherForFetchStatus(error.status)) { - failWatcher(watcher, createStreamStatusError(error.status).details); - } - return null; - } - log.warn("Cloud task status fetch error", { - runId: watcher.runId, - error, - }); - return null; - } -} - -/** - * Loads the historical log entries for the run, mirroring the desktop's - * dual-source strategy: - * 1. Try the paginated `session_logs/` API — the live source while a run - * is active. For older / archived runs this can come back empty even - * though the canonical log exists on S3. - * 2. Fall back to the run's presigned `log_url` (S3 NDJSON), which is the - * canonical archive for completed runs. - * - * Returns `null` only when both sources fail outright (so the bootstrap can - * surface a retryable error). An empty paginated result is treated as "no - * data yet" and falls through to S3 — if S3 also has nothing we return the - * empty array so the snapshot can still flip the session to `"connected"`. - */ -async function fetchHistoricalEntries( - watcher: WatcherState, - run: TaskRun, -): Promise { - const paginated = await fetchAllSessionLogs(watcher); - if (watcher.stopped || watcher.failed) return null; - if (paginated && paginated.length > 0) return paginated; - - if (run.log_url) { - const s3Entries = await fetchS3LogEntries(watcher, run.log_url); - if (watcher.stopped || watcher.failed) return null; - if (s3Entries && s3Entries.length > 0) return s3Entries; - } - - // Both sources returned no rows. Prefer the paginated result (which is - // `[]` rather than `null`) so the caller can still emit an empty snapshot - // and the session flips to `"connected"` instead of hanging on loading. - return paginated ?? null; -} - -async function fetchS3LogEntries( - watcher: WatcherState, - logUrl: string, -): Promise { - try { - const response = await fetch(logUrl, { - signal: createTimeoutSignal(15_000), - }); - if (response.status === 404) { - // No archived log yet for this run — not an error, just no data. - return []; - } - if (!response.ok) { - log.warn("S3 session log fetch returned non-OK", { - runId: watcher.runId, - status: response.status, - }); - return null; +export function watchCloudTask({ + taskId, + runId, + onUpdate, +}: WatchCloudTaskOptions): WatchCloudTaskHandle { + const engine = getCloudTaskEngine(); + const listener = (update: CloudTaskUpdatePayload): void => { + if (update.taskId === taskId && update.runId === runId) { + onUpdate(update); } - const content = await response.text(); - if (!content.trim()) return []; - return parseSessionLogs(content).rawEntries; - } catch (error) { - log.warn("S3 session log fetch failed", { - runId: watcher.runId, - error, - }); - return null; - } -} + }; -async function fetchAllSessionLogs( - watcher: WatcherState, -): Promise { - const entries: StoredLogEntry[] = []; - let offset = 0; + engine.on(CloudTaskEvent.Update, listener); + engine.watch({ + taskId, + runId, + apiHost: getBaseUrl(), + teamId: getProjectId(), + }); - while (true) { - if (watcher.stopped || watcher.failed) return null; - try { - const page = await fetchSessionLogs(watcher.taskId, watcher.runId, { - limit: SESSION_LOG_PAGE_LIMIT, - offset, - }); + let stopped = false; - for (const entry of page.entries) { - entries.push(entry); - } - if (!page.hasMore || page.entries.length === 0) { - return entries; + return { + stop: () => { + if (stopped) { + return; } - offset += page.entries.length; - } catch (error) { - if (error instanceof HttpError) { - log.warn("Cloud task session logs fetch failed", { - runId: watcher.runId, - status: error.status, - offset, - }); - if (shouldFailWatcherForFetchStatus(error.status)) { - failWatcher(watcher, createStreamStatusError(error.status).details); - } - return null; + stopped = true; + engine.off(CloudTaskEvent.Update, listener); + engine.unwatch(taskId, runId); + }, + reconnectIfDisconnected: () => { + if (!stopped) { + engine.reconnectIfDisconnected(taskId, runId); } - log.warn("Cloud task session logs fetch error", { - runId: watcher.runId, - offset, - error, - }); - return null; - } - } + }, + }; } diff --git a/apps/mobile/src/features/tasks/lib/sseParser.ts b/apps/mobile/src/features/tasks/lib/sseParser.ts deleted file mode 100644 index 4c626fd657..0000000000 --- a/apps/mobile/src/features/tasks/lib/sseParser.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { logger } from "@/lib/logger"; - -const log = logger.scope("sse-parser"); - -export interface SseEvent { - event?: string; - id?: string; - data: unknown; -} - -export class SseEventParser { - private buffer = ""; - private currentEventName: string | null = null; - private currentEventId: string | null = null; - private currentData: string[] = []; - - parse(chunk: string): SseEvent[] { - this.buffer += chunk; - const lines = this.buffer.split("\n"); - this.buffer = lines.pop() || ""; - - const events: SseEvent[] = []; - - for (const rawLine of lines) { - const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine; - - if (line === "") { - const event = this.flushEvent(); - if (event) { - events.push(event); - } - continue; - } - - if (line.startsWith(":")) { - continue; - } - - if (line.startsWith("event:")) { - this.currentEventName = line.slice(6).trim() || null; - continue; - } - - if (line.startsWith("id:")) { - this.currentEventId = line.slice(3).trim() || null; - continue; - } - - if (line.startsWith("data:")) { - this.currentData.push(line.slice(5).trimStart()); - } - } - - return events; - } - - reset(): void { - this.buffer = ""; - this.currentEventName = null; - this.currentEventId = null; - this.currentData = []; - } - - private flushEvent(): SseEvent | null { - if (this.currentData.length === 0) { - this.currentEventName = null; - this.currentEventId = null; - return null; - } - - const rawData = this.currentData.join("\n"); - this.currentData = []; - - try { - const data = JSON.parse(rawData); - return { - event: this.currentEventName ?? undefined, - id: this.currentEventId ?? undefined, - data, - }; - } catch { - log.warn("SSE event JSON parse failure", { rawData }); - return null; - } finally { - this.currentEventName = null; - this.currentEventId = null; - } - } -} diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts index b0b49e1ef1..864ca6271e 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts @@ -1,5 +1,13 @@ +import type { + CloudTaskUpdatePayload, + StoredLogEntry, + Task, + TaskRun, +} from "@posthog/shared"; import { beforeEach, describe, expect, it, vi } from "vitest"; +const { mockGetTask } = vi.hoisted(() => ({ mockGetTask: vi.fn() })); + vi.mock("expo-haptics", () => ({ impactAsync: vi.fn(), notificationAsync: vi.fn(), @@ -18,19 +26,16 @@ vi.mock("@/features/notifications/lib/notifications", () => ({ })); vi.mock("../api", () => ({ CloudCommandError: class CloudCommandError extends Error {}, - getTask: vi.fn(), runTaskInCloud: vi.fn(), sendCloudCommand: vi.fn(), })); +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ getTask: mockGetTask }), +})); + import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { getTask, runTaskInCloud } from "../api"; -import type { - CloudTaskUpdatePayload, - StoredLogEntry, - Task, - TaskRun, -} from "../types"; +import { runTaskInCloud } from "../api"; import { useMessageQueueStore } from "./messageQueueStore"; import { mapTerminalStatus, @@ -234,7 +239,6 @@ describe("flushQueuedMessagesIfIdle", () => { }); describe("_resumeCloudRun", () => { - const mockGetTask = vi.mocked(getTask); const mockRunTaskInCloud = vi.mocked(runTaskInCloud); function previousTask(latestRun: Partial): Task { diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts index e71f284ab5..a47638c679 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts @@ -1,13 +1,19 @@ +import { + type CloudTaskUpdatePayload, + isTerminalStatus, + type StoredLogEntry, + type Task, +} from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { AppState } from "react-native"; import { create } from "zustand"; import { presentLocalNotification } from "@/features/notifications/lib/notifications"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; import { logger } from "@/lib/logger"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { CloudCommandError, cancelRun, - getTask, runTaskInCloud, sendCloudCommand, } from "../api"; @@ -18,16 +24,12 @@ import { type WatchCloudTaskHandle, watchCloudTask, } from "../lib/cloudTaskStream"; -import { - type CloudPendingPermissionRequest, - type CloudTaskUpdatePayload, - isTerminalStatus, - type SessionEvent, - type SessionNotification, - type SessionNotificationAttachment, - type StoredLogEntry, - type Task, - type TerminalStatus, +import type { + CloudPendingPermissionRequest, + SessionEvent, + SessionNotification, + SessionNotificationAttachment, + TerminalStatus, } from "../types"; import { convertStoredEntriesToEvents } from "../utils/parseSessionLogs"; import { playbackRateForTaskDuration } from "../utils/playbackRate"; @@ -1178,7 +1180,7 @@ export const useTaskSessionStore = create((set, get) => ({ previousRunId: string, prompt: string, ) => { - const freshTask = await getTask(taskId); + const freshTask = await getPostHogApiClient().getTask(taskId); const previousRun = freshTask.latest_run; const previousBranch = previousRun?.branch ?? null; diff --git a/apps/mobile/src/features/tasks/stores/taskStore.test.ts b/apps/mobile/src/features/tasks/stores/taskStore.test.ts deleted file mode 100644 index 19f990c105..0000000000 --- a/apps/mobile/src/features/tasks/stores/taskStore.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { Task } from "../types"; -import { filterAndSortTasks } from "./taskStore"; - -function makeTask(overrides: Partial): Task { - return { - id: "task-1", - task_number: 1, - slug: "task-1", - title: "A real task", - description: "Do the thing", - created_at: "2026-01-01T00:00:00Z", - updated_at: "2026-01-01T00:00:00Z", - origin_product: "tasks", - ...overrides, - }; -} - -describe("filterAndSortTasks", () => { - it.each([ - { name: "empty title and description", title: "", description: "" }, - { name: "whitespace-only fields", title: " ", description: "\n\t" }, - ])( - "hides warm-sandbox placeholder tasks ($name)", - ({ title, description }) => { - const placeholder = makeTask({ id: "warm", title, description }); - const real = makeTask({ id: "real" }); - - const result = filterAndSortTasks( - [placeholder, real], - "updated", - false, - "", - ); - - expect(result.map((t) => t.id)).toEqual(["real"]); - }, - ); - - it.each([ - { - name: "description only (title not landed yet)", - title: "", - description: "Fix login", - }, - { name: "title only", title: "Fix login", description: "" }, - ])("keeps a real task with $name", ({ title, description }) => { - const task = makeTask({ id: "real", title, description }); - - const result = filterAndSortTasks([task], "updated", false, ""); - - expect(result.map((t) => t.id)).toEqual(["real"]); - }); -}); diff --git a/apps/mobile/src/features/tasks/stores/taskStore.ts b/apps/mobile/src/features/tasks/stores/taskStore.ts index 6c8daa14f2..456eae37a1 100644 --- a/apps/mobile/src/features/tasks/stores/taskStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskStore.ts @@ -1,9 +1,8 @@ -import { isContentlessTask } from "@posthog/shared/domain-types"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; import type { ExecutionMode, ReasoningEffort } from "../composer/options"; -import type { RepositorySelection, Task } from "../types"; +import type { RepositorySelection } from "../types"; export type OrganizeMode = "by-project" | "chronological"; export type SortMode = "created" | "updated"; @@ -106,48 +105,3 @@ export const useTaskStore = create()( }, ), ); - -export function taskActivityTimestamp(task: Task, sortMode: SortMode): number { - if (sortMode === "created") { - return new Date(task.created_at).getTime(); - } - // "updated" — take the most recent of task.updated_at and latest_run.updated_at. - const runUpdated = task.latest_run?.updated_at; - const taskUpdated = task.updated_at ?? task.created_at; - return Math.max( - runUpdated ? new Date(runUpdated).getTime() : 0, - new Date(taskUpdated).getTime(), - ); -} - -export function filterAndSortTasks( - tasks: Task[], - sortMode: SortMode, - showInternal: boolean, - filter: string, -): Task[] { - let filtered = tasks; - - // Warm-sandbox prewarming creates empty placeholder tasks; never surface them. - filtered = filtered.filter((task) => !isContentlessTask(task)); - - // Visibility filter — mirrors desktop radio: External hides internal, Internal shows only internal. - filtered = filtered.filter((task) => - showInternal ? task.internal === true : task.internal !== true, - ); - - if (filter) { - const lowerFilter = filter.toLowerCase(); - filtered = filtered.filter( - (task) => - task.title.toLowerCase().includes(lowerFilter) || - task.slug.toLowerCase().includes(lowerFilter) || - task.description?.toLowerCase().includes(lowerFilter), - ); - } - - return [...filtered].sort( - (a, b) => - taskActivityTimestamp(b, sortMode) - taskActivityTimestamp(a, sortMode), - ); -} diff --git a/apps/mobile/src/features/tasks/types.ts b/apps/mobile/src/features/tasks/types.ts index e777246331..29a2754d7f 100644 --- a/apps/mobile/src/features/tasks/types.ts +++ b/apps/mobile/src/features/tasks/types.ts @@ -1,115 +1,19 @@ -export interface Task { - id: string; - task_number: number | null; - slug: string; - title: string; - description: string; - created_at: string; - updated_at: string; - origin_product: string; - /** Inbox report UUID when origin_product is "signal_report". */ - signal_report?: string | null; - repository?: string | null; - github_integration?: number | null; - internal?: boolean; - latest_run?: TaskRun; -} - -export interface TaskAutomation { - id: string; - name: string; - prompt: string; - repository: string; - github_integration?: number | null; - cron_expression: string; - timezone?: string | null; - template_id?: string | null; - enabled: boolean; - last_run_at: string | null; - last_run_status: string | null; - last_task_id: string | null; - last_task_run_id: string | null; - last_error: string | null; - created_at: string; - updated_at: string; -} - -export type TaskRunStatus = - | "not_started" - | "queued" - | "started" - | "in_progress" - | "completed" - | "failed" - | "cancelled"; - -export const TERMINAL_STATUSES = ["completed", "failed", "cancelled"] as const; - -// UI-facing terminal outcome for a run. `cancelled` maps to `stopped` (the user -// deliberately halted it) so the UI can distinguish it from a real failure. -export type TerminalStatus = "completed" | "failed" | "stopped"; - -export function isTerminalStatus( - status: TaskRunStatus | string | null | undefined, -): boolean { - return ( - status !== null && - status !== undefined && - TERMINAL_STATUSES.includes(status as (typeof TERMINAL_STATUSES)[number]) - ); -} - -export interface TaskRunArtifact { - id?: string; - storage_path?: string; -} - -export interface TaskRun { - id: string; - task: string; - team: number; - branch: string | null; - stage?: string | null; - environment?: "local" | "cloud"; - status: TaskRunStatus; - log_url: string; - error_message: string | null; - reasoning_effort?: string | null; - output: Record | null; - state: Record; - artifacts?: TaskRunArtifact[]; - created_at: string; - updated_at: string; - completed_at: string | null; -} - -export interface StoredLogEntry { - type: string; - timestamp?: string; - notification?: { - id?: number; - method?: string; - params?: unknown; - result?: unknown; - error?: unknown; - }; +import type { + CloudPermissionOption, + CloudTaskPermissionRequestUpdate, + StoredLogEntry as SharedStoredLogEntry, + TaskRunStatus, +} from "@posthog/shared"; + +export interface MobileStoredLogEntry extends SharedStoredLogEntry { direction?: "client" | "agent"; } -export interface CloudArtifactRef { - runId: string; - artifactId: string; -} - export interface SessionNotificationAttachment { kind: "image" | "document"; uri: string; fileName: string; mimeType?: string; - // Set when the attachment was resolved from a cloud `session/prompt` entry. - // Its bytes live in S3 as a run artifact; the preview is fetched by presigning - // rather than read off the local device. - cloudArtifact?: CloudArtifactRef; } export interface SessionNotification { @@ -157,22 +61,6 @@ export interface SessionUpdateEvent { export type SessionEvent = AcpMessage | SessionUpdateEvent; -export interface CloudPermissionOption { - kind: string; - optionId: string; - name: string; - _meta?: Record; -} - -export interface CloudPermissionToolCall { - toolCallId: string; - title: string; - kind: string; - content?: unknown[]; - rawInput?: Record; - _meta?: Record; -} - export interface CloudPermissionResponseSelection { optionId: string; displayText: string; @@ -182,63 +70,11 @@ export interface CloudPermissionResponseSelection { export interface CloudPendingPermissionRequest { requestId: string; - toolCall: CloudPermissionToolCall; + toolCall: CloudTaskPermissionRequestUpdate["toolCall"]; options: CloudPermissionOption[]; response?: CloudPermissionResponseSelection; } -interface CloudTaskUpdateBase { - taskId: string; - runId: string; -} - -export interface CloudTaskLogsUpdate extends CloudTaskUpdateBase { - kind: "logs"; - newEntries: StoredLogEntry[]; - totalEntryCount: number; -} - -export interface CloudTaskStatusUpdate extends CloudTaskUpdateBase { - kind: "status"; - status?: TaskRunStatus; - stage?: string | null; - output?: Record | null; - errorMessage?: string | null; - branch?: string | null; -} - -export interface CloudTaskSnapshotUpdate extends CloudTaskUpdateBase { - kind: "snapshot"; - newEntries: StoredLogEntry[]; - totalEntryCount: number; - status?: TaskRunStatus; - stage?: string | null; - output?: Record | null; - errorMessage?: string | null; - branch?: string | null; -} - -export interface CloudTaskErrorUpdate extends CloudTaskUpdateBase { - kind: "error"; - errorTitle: string; - errorMessage: string; - retryable: boolean; -} - -export interface CloudTaskPermissionRequestUpdate extends CloudTaskUpdateBase { - kind: "permission_request"; - requestId: string; - toolCall: CloudPermissionToolCall; - options: CloudPermissionOption[]; -} - -export type CloudTaskUpdatePayload = - | CloudTaskLogsUpdate - | CloudTaskStatusUpdate - | CloudTaskSnapshotUpdate - | CloudTaskErrorUpdate - | CloudTaskPermissionRequestUpdate; - export interface TaskRunStateEvent { type: "task_run_state"; status?: TaskRunStatus; @@ -253,7 +89,7 @@ export interface TaskRunStateEvent { export interface PermissionRequestEventData { type: "permission_request"; requestId: string; - toolCall: CloudPermissionToolCall; + toolCall: CloudTaskPermissionRequestUpdate["toolCall"]; options: CloudPermissionOption[]; } @@ -344,25 +180,3 @@ export interface CreateTaskOptions { * cloud runs. Preferred over `github_integration` for interactive tasks. */ github_user_integration?: string; } - -export interface CreateTaskAutomationOptions { - name: string; - prompt: string; - repository: string; - github_integration?: number | null; - cron_expression: string; - timezone: string; - enabled?: boolean; - template_id?: string | null; -} - -export interface UpdateTaskAutomationOptions { - name?: string; - prompt?: string; - repository?: string; - github_integration?: number | null; - cron_expression?: string; - timezone?: string; - enabled?: boolean; - template_id?: string | null; -} diff --git a/apps/mobile/src/features/tasks/utils/archiveGuard.test.ts b/apps/mobile/src/features/tasks/utils/archiveGuard.test.ts index 6ad491c61f..730e0d98d6 100644 --- a/apps/mobile/src/features/tasks/utils/archiveGuard.test.ts +++ b/apps/mobile/src/features/tasks/utils/archiveGuard.test.ts @@ -1,58 +1,6 @@ import { Alert } from "react-native"; import { describe, expect, it, vi } from "vitest"; -import type { Task, TaskRunStatus } from "../types"; -import { confirmArchiveRunningTask, isTaskRunning } from "./archiveGuard"; - -function makeTask(status?: TaskRunStatus): Task { - return { - id: "task-1", - task_number: 1, - slug: "task-1", - title: "Test task", - description: "", - created_at: "2026-01-01T00:00:00Z", - updated_at: "2026-01-01T00:00:00Z", - origin_product: "code", - latest_run: status - ? { - id: "run-1", - task: "task-1", - team: 1, - branch: null, - stage: null, - environment: "cloud", - status, - log_url: "", - error_message: null, - output: null, - state: {}, - created_at: "2026-01-01T00:00:00Z", - updated_at: "2026-01-01T00:00:00Z", - completed_at: null, - } - : undefined, - }; -} - -describe("isTaskRunning", () => { - it("treats a task with no run as not running", () => { - expect(isTaskRunning(makeTask())).toBe(false); - }); - - it.each(["not_started", "queued", "started", "in_progress"] as const)( - "treats %s as running", - (status) => { - expect(isTaskRunning(makeTask(status))).toBe(true); - }, - ); - - it.each(["completed", "failed", "cancelled"] as const)( - "treats %s as not running", - (status) => { - expect(isTaskRunning(makeTask(status))).toBe(false); - }, - ); -}); +import { confirmArchiveRunningTask } from "./archiveGuard"; describe("confirmArchiveRunningTask", () => { it("archives only when the user confirms", () => { diff --git a/apps/mobile/src/features/tasks/utils/archiveGuard.ts b/apps/mobile/src/features/tasks/utils/archiveGuard.ts index 377b13dd47..d5e8cd9687 100644 --- a/apps/mobile/src/features/tasks/utils/archiveGuard.ts +++ b/apps/mobile/src/features/tasks/utils/archiveGuard.ts @@ -1,10 +1,4 @@ import { Alert } from "react-native"; -import { isTerminalStatus, type Task } from "../types"; - -export function isTaskRunning(task: Task): boolean { - const status = task.latest_run?.status; - return status !== undefined && !isTerminalStatus(status); -} export function confirmArchiveRunningTask( taskTitle: string, diff --git a/apps/mobile/src/features/tasks/utils/automationSchedule.test.ts b/apps/mobile/src/features/tasks/utils/automationSchedule.test.ts deleted file mode 100644 index af54fb0162..0000000000 --- a/apps/mobile/src/features/tasks/utils/automationSchedule.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - buildCronExpression, - createDefaultScheduleDraft, - deriveAutomationName, - formatScheduleSummary, - parseCronExpression, -} from "./automationSchedule"; - -describe("automationSchedule", () => { - it("builds cron expressions for common schedule presets", () => { - expect( - buildCronExpression({ - ...createDefaultScheduleDraft(), - mode: "hourly", - minute: "15", - }), - ).toBe("15 * * * *"); - - expect( - buildCronExpression({ - ...createDefaultScheduleDraft(), - mode: "daily", - hour: "09", - minute: "15", - }), - ).toBe("15 9 * * *"); - - expect( - buildCronExpression({ - ...createDefaultScheduleDraft(), - mode: "weekdays", - hour: "10", - minute: "00", - }), - ).toBe("0 10 * * 1-5"); - - expect( - buildCronExpression({ - ...createDefaultScheduleDraft(), - mode: "weekly", - hour: "11", - minute: "30", - weekday: "4", - }), - ).toBe("30 11 * * 4"); - }); - - it("parses common cron expressions back into schedule drafts", () => { - expect(parseCronExpression("15 * * * *")).toMatchObject({ - mode: "hourly", - minute: "15", - }); - - expect(parseCronExpression("0 9 * * *")).toMatchObject({ - mode: "daily", - hour: "09", - minute: "00", - }); - - expect(parseCronExpression("0 9 * * 1-5")).toMatchObject({ - mode: "weekdays", - hour: "09", - minute: "00", - }); - - expect(parseCronExpression("30 14 * * 2")).toMatchObject({ - mode: "weekly", - weekday: "2", - hour: "14", - minute: "30", - }); - }); - - it("keeps custom cron expressions in custom mode", () => { - expect(parseCronExpression("*/15 * * * *")).toMatchObject({ - mode: "custom", - rawCron: "*/15 * * * *", - }); - }); - - it("derives a readable automation name from the prompt", () => { - expect( - deriveAutomationName( - "\n Review every open PostHog PR for stale comments \n", - ), - ).toBe("Review every open PostHog PR for stale comments"); - }); - - it("formats schedule summaries with timezone context", () => { - expect(formatScheduleSummary("15 * * * *", "Europe/London")).toBe( - "Every hour at :15 · Europe/London", - ); - expect(formatScheduleSummary("0 9 * * 1-5", "Europe/London")).toBe( - "Weekdays at 09:00 · Europe/London", - ); - expect(formatScheduleSummary("*/15 * * * *", "UTC")).toBe( - "Custom schedule · UTC", - ); - }); -}); diff --git a/apps/mobile/src/features/tasks/utils/automationSchedule.ts b/apps/mobile/src/features/tasks/utils/automationSchedule.ts deleted file mode 100644 index 06cec5faf3..0000000000 --- a/apps/mobile/src/features/tasks/utils/automationSchedule.ts +++ /dev/null @@ -1,213 +0,0 @@ -import type { TaskAutomation } from "../types"; - -export type AutomationScheduleMode = - | "hourly" - | "daily" - | "weekdays" - | "weekly" - | "custom"; - -export interface AutomationScheduleDraft { - mode: AutomationScheduleMode; - hour: string; - minute: string; - weekday: string; - rawCron: string; -} - -export const WEEKDAY_OPTIONS = [ - { value: "1", label: "Mon" }, - { value: "2", label: "Tue" }, - { value: "3", label: "Wed" }, - { value: "4", label: "Thu" }, - { value: "5", label: "Fri" }, - { value: "6", label: "Sat" }, - { value: "0", label: "Sun" }, -] as const; - -export function createDefaultScheduleDraft(): AutomationScheduleDraft { - return { - mode: "daily", - hour: "09", - minute: "00", - weekday: "1", - rawCron: "0 9 * * *", - }; -} - -function padTimePart(value: string): string { - return value.padStart(2, "0"); -} - -export function sanitizeHour(value: string): string { - const digitsOnly = value.replace(/\D/g, "").slice(0, 2); - if (!digitsOnly) { - return ""; - } - - return String(Math.min(23, Number(digitsOnly))).padStart(2, "0"); -} - -export function sanitizeMinute(value: string): string { - const digitsOnly = value.replace(/\D/g, "").slice(0, 2); - if (!digitsOnly) { - return ""; - } - - return String(Math.min(59, Number(digitsOnly))).padStart(2, "0"); -} - -export function buildCronExpression(draft: AutomationScheduleDraft): string { - if (draft.mode === "custom") { - return draft.rawCron.trim(); - } - - const minute = draft.minute ? String(Number(draft.minute)) : "0"; - const hour = draft.hour ? String(Number(draft.hour)) : "9"; - - switch (draft.mode) { - case "hourly": - return `${minute} * * * *`; - case "weekdays": - return `${minute} ${hour} * * 1-5`; - case "weekly": - return `${minute} ${hour} * * ${draft.weekday || "1"}`; - default: - return `${minute} ${hour} * * *`; - } -} - -export function parseCronExpression( - cronExpression: string, -): AutomationScheduleDraft { - const normalized = cronExpression.trim(); - const parts = normalized.split(/\s+/); - - if (parts.length !== 5) { - return { - ...createDefaultScheduleDraft(), - mode: "custom", - rawCron: normalized, - }; - } - - const [minute, hour, dayOfMonth, month, dayOfWeek] = parts; - const isNumericMinute = /^\d{1,2}$/.test(minute); - const isNumericHour = /^\d{1,2}$/.test(hour); - const draftBase = { - hour: padTimePart(hour), - minute: padTimePart(minute), - weekday: dayOfWeek, - rawCron: normalized, - }; - - if ( - isNumericMinute && - hour === "*" && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - return { - ...draftBase, - mode: "hourly", - hour: "09", - }; - } - - if ( - isNumericMinute && - isNumericHour && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - return { - ...draftBase, - mode: "daily", - }; - } - - if ( - isNumericMinute && - isNumericHour && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "1-5" - ) { - return { - ...draftBase, - mode: "weekdays", - weekday: "1", - }; - } - - if ( - isNumericMinute && - isNumericHour && - dayOfMonth === "*" && - month === "*" && - /^\d$/.test(dayOfWeek) - ) { - return { - ...draftBase, - mode: "weekly", - }; - } - - return { - ...draftBase, - mode: "custom", - }; -} - -export function deriveAutomationName(prompt: string): string { - const normalized = prompt - .split("\n") - .map((line) => line.trim()) - .find(Boolean); - - if (!normalized) { - return ""; - } - - return normalized.replace(/\s+/g, " ").slice(0, 80); -} - -function formatTime(hour: string, minute: string): string { - return `${padTimePart(hour)}:${padTimePart(minute)}`; -} - -export function formatScheduleSummary( - cronExpression: string, - timezone: string | null | undefined, -): string { - const draft = parseCronExpression(cronExpression); - const suffix = timezone ? ` · ${timezone}` : ""; - - switch (draft.mode) { - case "hourly": - return `Every hour at :${padTimePart(draft.minute)}${suffix}`; - case "weekdays": - return `Weekdays at ${formatTime(draft.hour, draft.minute)}${suffix}`; - case "weekly": { - const label = - WEEKDAY_OPTIONS.find((option) => option.value === draft.weekday) - ?.label ?? "Weekly"; - return `${label} at ${formatTime(draft.hour, draft.minute)}${suffix}`; - } - case "custom": - return `Custom schedule${suffix}`; - default: - return `Daily at ${formatTime(draft.hour, draft.minute)}${suffix}`; - } -} - -export function formatAutomationScheduleSummary( - automation: Pick, -): string { - return formatScheduleSummary( - automation.cron_expression, - automation.timezone ?? null, - ); -} diff --git a/apps/mobile/src/features/tasks/utils/automationStatus.test.ts b/apps/mobile/src/features/tasks/utils/automationStatus.test.ts index 3a3305451b..7a24d5d050 100644 --- a/apps/mobile/src/features/tasks/utils/automationStatus.test.ts +++ b/apps/mobile/src/features/tasks/utils/automationStatus.test.ts @@ -8,9 +8,7 @@ describe("automationStatus", () => { lastRunStatus: "running", lastTaskRunStatus: "queued", }), - ).toMatchObject({ - label: "Queued", - }); + ).toMatchObject({ label: "Queued" }); }); it("hides the running badge while the linked task run is actively in progress", () => { @@ -24,19 +22,13 @@ describe("automationStatus", () => { it("hides the running badge when only the automation-level status is available", () => { expect( - getAutomationStatusPresentation({ - lastRunStatus: "running", - }), + getAutomationStatusPresentation({ lastRunStatus: "running" }), ).toBeNull(); }); it("falls back to automation status when task-run detail is unavailable", () => { expect( - getAutomationStatusPresentation({ - lastRunStatus: "success", - }), - ).toMatchObject({ - label: "Success", - }); + getAutomationStatusPresentation({ lastRunStatus: "success" }), + ).toMatchObject({ label: "Success" }); }); }); diff --git a/apps/mobile/src/features/tasks/utils/automationStatus.ts b/apps/mobile/src/features/tasks/utils/automationStatus.ts index e5dd7c8fe3..5e8b7f553f 100644 --- a/apps/mobile/src/features/tasks/utils/automationStatus.ts +++ b/apps/mobile/src/features/tasks/utils/automationStatus.ts @@ -1,4 +1,4 @@ -import type { TaskRun } from "../types"; +import type { TaskRun } from "@posthog/shared"; export interface AutomationStatusInput { lastRunStatus: string | null; @@ -21,7 +21,6 @@ export function getAutomationStatusPresentation({ label: "Queued", className: "bg-status-warning/20 text-status-warning", }; - case "started": case "in_progress": return null; case "completed": diff --git a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts index d899a29b9b..2f42d89acc 100644 --- a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts +++ b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts @@ -1,5 +1,5 @@ +import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { parseSkillTemplateId } from "../skills/skillTemplateIds"; -import type { TaskAutomation } from "../types"; export interface AutomationTemplatePresentation { templateName: string | null; diff --git a/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts b/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts index 9cce3995d2..a6d512d59d 100644 --- a/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts +++ b/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts @@ -1,12 +1,9 @@ -import type { - SessionEvent, - SessionNotification, - StoredLogEntry, -} from "../types"; +import { convertStoredEntriesToPortableSessionEvents } from "@posthog/core/sessions/portableSessionEvents"; +import type { MobileStoredLogEntry, SessionNotification } from "../types"; export interface ParsedSessionLogs { notifications: SessionNotification[]; - rawEntries: StoredLogEntry[]; + rawEntries: MobileStoredLogEntry[]; } export function parseSessionLogs(content: string): ParsedSessionLogs { @@ -15,11 +12,11 @@ export function parseSessionLogs(content: string): ParsedSessionLogs { } const notifications: SessionNotification[] = []; - const rawEntries: StoredLogEntry[] = []; + const rawEntries: MobileStoredLogEntry[] = []; for (const line of content.trim().split("\n")) { try { - const stored = JSON.parse(line) as StoredLogEntry; + const stored = JSON.parse(line) as MobileStoredLogEntry; const msg = stored.notification; if (msg) { @@ -53,81 +50,5 @@ export function parseSessionLogs(content: string): ParsedSessionLogs { return { notifications, rawEntries }; } -export function convertRawEntriesToEvents( - rawEntries: StoredLogEntry[], - notifications: SessionNotification[], -): SessionEvent[] { - const events: SessionEvent[] = []; - let notificationIdx = 0; - - for (const entry of rawEntries) { - const ts = entry.timestamp - ? new Date(entry.timestamp).getTime() - : Date.now(); - - events.push({ - type: "acp_message", - direction: entry.direction ?? "agent", - ts, - message: entry.notification, - }); - - if ( - entry.type === "notification" && - entry.notification?.method === "session/update" && - notificationIdx < notifications.length - ) { - events.push({ - type: "session_update", - ts, - notification: notifications[notificationIdx], - }); - notificationIdx++; - } - } - - return events; -} - -function inferDirection(entry: StoredLogEntry): "client" | "agent" { - if (entry.direction) return entry.direction; - const msg = entry.notification; - if (!msg) return "agent"; - const hasId = msg.id !== undefined; - const hasMethod = msg.method !== undefined; - const hasResult = msg.result !== undefined || msg.error !== undefined; - if (hasId && hasMethod) return "client"; - if (hasId && hasResult) return "agent"; - return "agent"; -} - -export function convertStoredEntriesToEvents( - entries: StoredLogEntry[], -): SessionEvent[] { - const events: SessionEvent[] = []; - for (const entry of entries) { - const ts = entry.timestamp - ? new Date(entry.timestamp).getTime() - : Date.now(); - - events.push({ - type: "acp_message", - direction: inferDirection(entry), - ts, - message: entry.notification, - }); - - if ( - entry.type === "notification" && - entry.notification?.method === "session/update" && - entry.notification?.params - ) { - events.push({ - type: "session_update", - ts, - notification: entry.notification.params as SessionNotification, - }); - } - } - return events; -} +export const convertStoredEntriesToEvents = + convertStoredEntriesToPortableSessionEvents; diff --git a/apps/mobile/src/features/tasks/utils/sessionActivity.test.ts b/apps/mobile/src/features/tasks/utils/sessionActivity.test.ts deleted file mode 100644 index aef0f08b44..0000000000 --- a/apps/mobile/src/features/tasks/utils/sessionActivity.test.ts +++ /dev/null @@ -1,161 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { SessionEvent } from "../types"; -import { - countUserMessages, - getSessionActivityPhase, - isSessionAwaitingUserInput, -} from "./sessionActivity"; - -function buildUserMessage(text: string): SessionEvent { - return { - type: "session_update", - ts: 1, - notification: { - update: { - sessionUpdate: "user_message_chunk", - content: { type: "text", text }, - }, - }, - } satisfies SessionEvent; -} - -describe("countUserMessages", () => { - it("counts only user_message_chunk events", () => { - expect( - countUserMessages([ - buildUserMessage("hello"), - buildQuestionToolCall("pending"), - buildUserMessage("again"), - ]), - ).toBe(2); - }); - - it("returns 0 for no events", () => { - expect(countUserMessages()).toBe(0); - }); -}); - -function buildQuestionToolCall( - status: "pending" | "in_progress" | "completed", -) { - return { - type: "session_update", - ts: 1, - notification: { - update: { - sessionUpdate: "tool_call", - toolCallId: "question-1", - status, - rawInput: { - questions: [{ question: "Proceed?", options: [] }], - }, - _meta: { - claudeCode: { - toolName: "AskUserQuestion", - }, - }, - }, - }, - } satisfies SessionEvent; -} - -describe("getSessionActivityPhase", () => { - it("treats retrying as connecting", () => { - expect( - getSessionActivityPhase({ - retrying: true, - session: { isPromptPending: true, awaitingAgentOutput: false }, - }), - ).toBe("connecting"); - }); - - it("stays connecting until the agent emits visible output", () => { - expect( - getSessionActivityPhase({ - retrying: false, - session: { isPromptPending: true, awaitingAgentOutput: true }, - }), - ).toBe("connecting"); - }); - - it("shows working only after the agent is actively in a turn", () => { - expect( - getSessionActivityPhase({ - retrying: false, - session: { isPromptPending: true, awaitingAgentOutput: false }, - }), - ).toBe("working"); - }); - - it("returns idle once the agent is no longer working", () => { - expect( - getSessionActivityPhase({ - retrying: false, - session: { isPromptPending: false, awaitingAgentOutput: false }, - }), - ).toBe("idle"); - - expect( - getSessionActivityPhase({ - retrying: false, - session: { - isPromptPending: true, - awaitingAgentOutput: false, - terminalStatus: "completed", - }, - }), - ).toBe("idle"); - }); - - it("returns idle while the agent is paused on a question tool", () => { - expect( - getSessionActivityPhase({ - retrying: false, - session: { - isPromptPending: true, - awaitingAgentOutput: false, - events: [buildQuestionToolCall("pending")], - }, - }), - ).toBe("idle"); - }); -}); - -describe("isSessionAwaitingUserInput", () => { - it("detects unresolved question tools", () => { - expect(isSessionAwaitingUserInput([buildQuestionToolCall("pending")])).toBe( - true, - ); - }); - - it("clears the waiting state once the user responds", () => { - const events: SessionEvent[] = [ - buildQuestionToolCall("pending"), - { - type: "session_update", - ts: 2, - notification: { - update: { - sessionUpdate: "user_message_chunk", - content: { type: "text", text: "Yes" }, - }, - }, - }, - ]; - - expect(isSessionAwaitingUserInput(events)).toBe(false); - }); - - it("honors explicit awaiting-user-input backend markers", () => { - const events: SessionEvent[] = [ - { - type: "acp_message", - direction: "agent", - ts: 1, - message: { method: "_posthog/awaiting_user_input" }, - }, - ]; - - expect(isSessionAwaitingUserInput(events)).toBe(true); - }); -}); diff --git a/apps/mobile/src/features/tasks/utils/sessionActivity.ts b/apps/mobile/src/features/tasks/utils/sessionActivity.ts deleted file mode 100644 index 982d4db8b6..0000000000 --- a/apps/mobile/src/features/tasks/utils/sessionActivity.ts +++ /dev/null @@ -1,133 +0,0 @@ -import type { - SessionEvent, - SessionNotification, - TerminalStatus, -} from "../types"; - -export type SessionActivityPhase = "idle" | "connecting" | "working"; - -interface SessionActivityState { - isPromptPending?: boolean; - awaitingAgentOutput?: boolean; - terminalStatus?: TerminalStatus; - events?: SessionEvent[]; -} - -function isQuestionNotification(notification: SessionNotification): boolean { - const update = notification.update; - if (!update) return false; - - const rawToolName = update._meta?.claudeCode?.toolName; - if (typeof rawToolName === "string" && /question/i.test(rawToolName)) { - return true; - } - - const rawInput = update.rawInput; - if (!rawInput) return false; - - if (Array.isArray(rawInput.questions)) { - return true; - } - - const nestedInput = rawInput.input; - return ( - typeof nestedInput === "object" && - nestedInput !== null && - Array.isArray((nestedInput as { questions?: unknown }).questions) - ); -} - -function isPendingQuestionStatus( - status?: "pending" | "in_progress" | "completed" | "failed" | null, -): boolean { - return status === null || status === "pending" || status === "in_progress"; -} - -export function isSessionAwaitingUserInput( - events: SessionEvent[] = [], -): boolean { - let awaitingUserInput = false; - const questionStatuses = new Map< - string, - "pending" | "in_progress" | "completed" | "failed" | null | undefined - >(); - - for (const event of events) { - if (event.type === "session_update") { - const update = event.notification.update; - const sessionUpdate = update?.sessionUpdate; - - if (sessionUpdate === "user_message_chunk") { - awaitingUserInput = false; - questionStatuses.clear(); - continue; - } - - if ( - (sessionUpdate === "tool_call" || - sessionUpdate === "tool_call_update") && - isQuestionNotification(event.notification) - ) { - questionStatuses.set( - update?.toolCallId ?? `question-${event.ts}`, - update?.status, - ); - awaitingUserInput = [...questionStatuses.values()].some((status) => - isPendingQuestionStatus(status), - ); - } - - continue; - } - - const method = - event.message && typeof event.message === "object" - ? (event.message as { method?: string }).method - : undefined; - - if (method === "_posthog/awaiting_user_input") { - awaitingUserInput = true; - continue; - } - - if ( - method === "_posthog/turn_complete" || - method === "_posthog/task_complete" || - method === "_posthog/error" - ) { - awaitingUserInput = false; - questionStatuses.clear(); - } - } - - return awaitingUserInput; -} - -export function countUserMessages(events: SessionEvent[] = []): number { - return events.filter( - (e) => - e.type === "session_update" && - e.notification.update?.sessionUpdate === "user_message_chunk", - ).length; -} - -export function getSessionActivityPhase(args: { - retrying: boolean; - session?: SessionActivityState | null; -}): SessionActivityPhase { - const { retrying, session } = args; - - if (retrying) { - return "connecting"; - } - - if (!session?.isPromptPending || session.terminalStatus) { - return "idle"; - } - - if (isSessionAwaitingUserInput(session.events)) { - return "idle"; - } - - return session.awaitingAgentOutput ? "connecting" : "working"; -} diff --git a/apps/mobile/src/lib/analytics.ts b/apps/mobile/src/lib/analytics.ts index 0bee789344..e7665ba661 100644 --- a/apps/mobile/src/lib/analytics.ts +++ b/apps/mobile/src/lib/analytics.ts @@ -1,5 +1,4 @@ -import type { PostHogEventProperties } from "@posthog/core"; -import { usePostHog } from "posthog-react-native"; +import { type PostHog, usePostHog } from "posthog-react-native"; import { useEffect, useMemo } from "react"; /** @@ -199,6 +198,8 @@ export interface Analytics { ): void; } +type PostHogCaptureProperties = Parameters[1]; + // Client discriminator stamped on inbox events so the shared PostHog project // can be sliced by surface (desktop sends "code", the web frontend sends // "cloud"). Mirrors packages/ui/src/shell/posthogAnalyticsImpl.ts. @@ -221,12 +222,9 @@ export function useAnalytics(): Analytics { const enriched = INBOX_ANALYTICS_EVENT_NAMES.has(eventName) ? { inbox_client: INBOX_CLIENT, ...properties } : properties; - // Our typed property interfaces don't carry an index signature; cast - // to the wider PostHog event-properties shape without losing the - // narrower call-site type-check. posthog?.capture( eventName, - enriched as unknown as PostHogEventProperties, + enriched as unknown as PostHogCaptureProperties, ); }, }), diff --git a/apps/mobile/src/lib/api.ts b/apps/mobile/src/lib/api.ts index 58603788d3..ccc2a97a09 100644 --- a/apps/mobile/src/lib/api.ts +++ b/apps/mobile/src/lib/api.ts @@ -3,9 +3,20 @@ import Constants from "expo-constants"; import { useAuthStore } from "@/features/auth"; import { logger } from "@/lib/logger"; +export class HttpError extends Error { + constructor( + readonly status: number, + readonly statusText: string, + message: string, + ) { + super(message); + this.name = "HttpError"; + } +} + // Derive the init shape directly from expo/fetch so we don't import from // expo's internal build output (which can move between versions). -type FetchInit = NonNullable[1]>; +export type FetchInit = NonNullable[1]>; const log = logger.scope("api"); @@ -66,7 +77,7 @@ export function createTimeoutSignal(ms: number): AbortSignal { // pending refresh across all callers and reset it once it settles. let pendingRefresh: Promise | null = null; -async function refreshAccessTokenOnce(): Promise { +export async function refreshAccessTokenOnce(): Promise { if (pendingRefresh) return pendingRefresh; const promise = useAuthStore .getState() diff --git a/apps/mobile/src/lib/posthogApiClient.test.ts b/apps/mobile/src/lib/posthogApiClient.test.ts new file mode 100644 index 0000000000..2aae2a0ed2 --- /dev/null +++ b/apps/mobile/src/lib/posthogApiClient.test.ts @@ -0,0 +1,190 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + authState: { + cloudRegion: "us" as string | null, + getCloudUrlFromRegion: vi.fn(() => "https://us.posthog.com"), + oauthAccessToken: "access-token" as string | null, + projectId: 123 as number | null, + refreshAccessToken: vi.fn(async () => {}), + }, + expoApplication: { + nativeApplicationVersion: "1.2.3" as string | null, + }, + expoConstants: { + expoConfig: { version: "9.9.9" } as { version?: string } | null, + }, + expoFetch: vi.fn(), + instances: [] as Array<{ + apiHost: string; + getAccessToken: () => Promise; + refreshAccessToken: () => Promise; + teamId: number | undefined; + options: Record; + setTeamId: ReturnType; + }>, +})); + +vi.mock("@posthog/api-client/posthog-client", () => ({ + PostHogAPIClient: class { + setTeamId = vi.fn(); + + constructor( + apiHost: string, + getAccessToken: () => Promise, + refreshAccessToken: () => Promise, + teamId: number | undefined, + options: Record, + ) { + mocks.instances.push({ + apiHost, + getAccessToken, + refreshAccessToken, + teamId, + options, + setTeamId: this.setTeamId, + }); + } + }, +})); + +vi.mock("expo-application", () => ({ + get nativeApplicationVersion() { + return mocks.expoApplication.nativeApplicationVersion; + }, +})); + +vi.mock("expo-constants", () => ({ + default: { + get expoConfig() { + return mocks.expoConstants.expoConfig; + }, + }, +})); + +vi.mock("expo/fetch", () => ({ fetch: mocks.expoFetch })); + +vi.mock("@/features/auth", () => ({ + useAuthStore: { + getState: () => mocks.authState, + }, +})); + +beforeEach(() => { + vi.resetModules(); + vi.clearAllMocks(); + mocks.instances.length = 0; + mocks.authState.cloudRegion = "us"; + mocks.authState.oauthAccessToken = "access-token"; + mocks.authState.projectId = 123; + mocks.authState.getCloudUrlFromRegion.mockReturnValue( + "https://us.posthog.com", + ); + mocks.authState.refreshAccessToken.mockImplementation(async () => {}); + mocks.expoApplication.nativeApplicationVersion = "1.2.3"; + mocks.expoConstants.expoConfig = { version: "9.9.9" }; +}); + +describe("createPostHogApiClient", () => { + it("configures the shared client for the mobile host", async () => { + const { createPostHogApiClient } = await import("./posthogApiClient"); + + createPostHogApiClient(); + + expect(mocks.instances).toHaveLength(1); + expect(mocks.instances[0]).toMatchObject({ + apiHost: "https://us.posthog.com", + teamId: 123, + options: { + appVersion: "1.2.3", + fetch: mocks.expoFetch, + githubConnectFrom: "posthog_mobile", + userAgent: "posthog/mobile.hog.dev; version: 1.2.3", + }, + }); + }); + + it("falls back to the Expo config version", async () => { + mocks.expoApplication.nativeApplicationVersion = null; + mocks.expoConstants.expoConfig = { version: "4.5.6" }; + const { createPostHogApiClient } = await import("./posthogApiClient"); + + createPostHogApiClient(); + + expect(mocks.instances[0]?.options).toMatchObject({ + appVersion: "4.5.6", + userAgent: "posthog/mobile.hog.dev; version: 4.5.6", + }); + }); + + it("returns the refreshed token from the current auth store state", async () => { + mocks.authState.refreshAccessToken.mockImplementation(async () => { + mocks.authState.oauthAccessToken = "refreshed-token"; + }); + const { createPostHogApiClient } = await import("./posthogApiClient"); + createPostHogApiClient(); + + await expect(mocks.instances[0]?.refreshAccessToken()).resolves.toBe( + "refreshed-token", + ); + expect(mocks.authState.refreshAccessToken).toHaveBeenCalledOnce(); + }); + + it("shares one refresh across concurrent client retries", async () => { + let resolveRefresh: (() => void) | undefined; + mocks.authState.refreshAccessToken.mockImplementation( + () => + new Promise((resolve) => { + resolveRefresh = () => { + mocks.authState.oauthAccessToken = "refreshed-token"; + resolve(); + }; + }), + ); + const { createPostHogApiClient } = await import("./posthogApiClient"); + createPostHogApiClient(); + + const refreshes = [ + mocks.instances[0]?.refreshAccessToken(), + mocks.instances[0]?.refreshAccessToken(), + ]; + expect(mocks.authState.refreshAccessToken).toHaveBeenCalledOnce(); + resolveRefresh?.(); + + await expect(Promise.all(refreshes)).resolves.toEqual([ + "refreshed-token", + "refreshed-token", + ]); + }); +}); + +describe("getPostHogApiClient", () => { + it("reuses the regional client and updates its project", async () => { + const { getPostHogApiClient } = await import("./posthogApiClient"); + + const first = getPostHogApiClient(); + mocks.authState.projectId = 456; + const second = getPostHogApiClient(); + + expect(second).toBe(first); + expect(mocks.instances).toHaveLength(1); + expect(mocks.instances[0]?.setTeamId).toHaveBeenCalledWith(456); + }); + + it("creates a new client when the cloud region changes", async () => { + const { getPostHogApiClient } = await import("./posthogApiClient"); + + const first = getPostHogApiClient(); + mocks.authState.cloudRegion = "eu"; + mocks.authState.getCloudUrlFromRegion.mockReturnValue( + "https://eu.posthog.com", + ); + const second = getPostHogApiClient(); + + expect(second).not.toBe(first); + expect(mocks.instances.map(({ apiHost }) => apiHost)).toEqual([ + "https://us.posthog.com", + "https://eu.posthog.com", + ]); + }); +}); diff --git a/apps/mobile/src/lib/posthogApiClient.ts b/apps/mobile/src/lib/posthogApiClient.ts new file mode 100644 index 0000000000..c094452f44 --- /dev/null +++ b/apps/mobile/src/lib/posthogApiClient.ts @@ -0,0 +1,84 @@ +import type { FetchImplementation } from "@posthog/api-client/fetcher"; +import { PostHogAPIClient } from "@posthog/api-client/posthog-client"; +import { fetch } from "expo/fetch"; +import * as Application from "expo-application"; +import Constants from "expo-constants"; +import { useAuthStore } from "@/features/auth"; +import { refreshAccessTokenOnce } from "@/lib/api"; + +const MOBILE_GITHUB_CONNECT_FROM = "posthog_mobile"; + +let posthogApiClient: PostHogAPIClient | null = null; +let posthogApiHost: string | null = null; + +function getAppVersion(): string { + return ( + Application.nativeApplicationVersion ?? + Constants.expoConfig?.version ?? + "unknown" + ); +} + +function getAuthenticatedContext(): { + apiHost: string; + projectId: number; +} { + const { cloudRegion, getCloudUrlFromRegion, projectId } = + useAuthStore.getState(); + + if (!cloudRegion) { + throw new Error("No cloud region set"); + } + if (!projectId) { + throw new Error("No project ID set"); + } + + return { + apiHost: getCloudUrlFromRegion(cloudRegion), + projectId, + }; +} + +async function getAccessToken(): Promise { + const { oauthAccessToken } = useAuthStore.getState(); + if (!oauthAccessToken) { + throw new Error("Not authenticated"); + } + return oauthAccessToken; +} + +async function refreshAccessToken(): Promise { + await refreshAccessTokenOnce(); + return getAccessToken(); +} + +export function createPostHogApiClient(): PostHogAPIClient { + const { apiHost, projectId } = getAuthenticatedContext(); + const appVersion = getAppVersion(); + + return new PostHogAPIClient( + apiHost, + getAccessToken, + refreshAccessToken, + projectId, + { + appVersion, + fetch: fetch as FetchImplementation, + githubConnectFrom: MOBILE_GITHUB_CONNECT_FROM, + userAgent: `posthog/mobile.hog.dev; version: ${appVersion}`, + }, + ); +} + +export function getPostHogApiClient(): PostHogAPIClient { + const { apiHost, projectId } = getAuthenticatedContext(); + + if (!posthogApiClient || posthogApiHost !== apiHost) { + posthogApiClient = createPostHogApiClient(); + posthogApiHost = apiHost; + } else { + posthogApiClient.setTeamId(projectId); + } + + return posthogApiClient; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cdfefdf93..4f35ccf04a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -479,6 +479,12 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.29.0 version: 1.29.0(zod@4.4.3) + '@posthog/api-client': + specifier: workspace:* + version: link:../../packages/api-client + '@posthog/core': + specifier: workspace:* + version: link:../../packages/core '@posthog/shared': specifier: workspace:* version: link:../../packages/shared From 5299c52f6cf4090546cba9f0f6d6cf8881013843 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 01:29:34 +0300 Subject: [PATCH 25/42] fix(mobile): preserve permission mode on resume Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts | 1 + apps/mobile/src/features/tasks/stores/taskSessionStore.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts index 864ca6271e..c38504a1c3 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts @@ -274,6 +274,7 @@ describe("_resumeCloudRun", () => { expect(mockRunTaskInCloud).toHaveBeenCalledWith("t1", { branch: "feature", + runtimeAdapter: "claude", resumeFromRunId: "prev-run", pendingUserMessage: "hi", reasoningEffort: "low", diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts index a47638c679..462589a58e 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts @@ -1197,6 +1197,7 @@ export const useTaskSessionStore = create((set, get) => ({ const updatedTask = await runTaskInCloud(taskId, { branch: previousBranch, + runtimeAdapter: "claude", resumeFromRunId: previousRunId, pendingUserMessage: prompt, reasoningEffort, From 2b5d2a50f9d7262a2021552a20202453cf7d6ead Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:30:06 +0300 Subject: [PATCH 26/42] refactor(mobile): finish shared task adoption Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/(tabs)/inbox.tsx | 10 +- apps/mobile/src/app/inbox/[...id].tsx | 24 +- apps/mobile/src/app/task/[id].tsx | 68 +++--- apps/mobile/src/app/task/index.tsx | 125 +++++++---- .../src/features/inbox/activityLog.test.ts | 14 +- apps/mobile/src/features/inbox/activityLog.ts | 6 +- apps/mobile/src/features/inbox/api.ts | 21 +- .../inbox/components/ArchivedReportList.tsx | 11 +- .../inbox/components/ArtefactCommit.tsx | 2 +- .../inbox/components/ArtefactTaskRun.tsx | 2 +- .../inbox/components/DismissReportSheet.tsx | 8 +- .../inbox/components/EditReviewersSheet.tsx | 5 +- .../features/inbox/components/FilterSheet.tsx | 29 +-- .../inbox/components/ReportActivity.tsx | 4 +- .../features/inbox/components/ReportList.tsx | 2 +- .../inbox/components/ReportListRow.tsx | 2 +- .../features/inbox/components/SignalCard.tsx | 5 +- .../inbox/components/SuggestedReviewers.tsx | 10 +- .../inbox/components/SwipeableReportCard.tsx | 12 +- .../features/inbox/components/TinderView.tsx | 33 +-- apps/mobile/src/features/inbox/constants.ts | 22 -- .../hooks/useInboxEngagementTracker.test.ts | 2 +- .../inbox/hooks/useInboxEngagementTracker.ts | 2 +- .../inbox/hooks/useInboxReports.test.ts | 5 +- .../features/inbox/hooks/useInboxReports.ts | 45 ++-- .../inbox/stores/inboxFilterStore.test.ts | 3 +- .../features/inbox/stores/inboxFilterStore.ts | 18 +- .../src/features/inbox/stores/inboxStore.ts | 2 +- apps/mobile/src/features/inbox/types.ts | 209 ------------------ apps/mobile/src/features/inbox/utils.test.ts | 126 ++++++++--- apps/mobile/src/features/inbox/utils.ts | 72 +----- .../tasks/composer/TaskChatComposer.tsx | 105 ++++++--- .../tasks/composer/attachments/cloudPrompt.ts | 16 -- .../features/tasks/composer/options.test.ts | 79 +++++-- .../src/features/tasks/composer/options.ts | 133 ++++------- .../tasks/hooks/useAutomations.test.ts | 24 +- .../hooks/useCloudTaskConfigOptions.test.ts | 121 ++++++++++ .../tasks/hooks/useCloudTaskConfigOptions.ts | 52 +++++ .../tasks/hooks/useIntegrations.test.ts | 6 +- .../features/tasks/hooks/useIntegrations.ts | 31 +-- .../src/features/tasks/hooks/useTasks.test.ts | 9 +- .../src/features/tasks/hooks/useTasks.ts | 16 +- .../tasks/hooks/useUserIntegrations.ts | 31 ++- .../features/tasks/hooks/useWarmTask.test.tsx | 4 +- apps/mobile/src/features/tasks/index.ts | 6 - .../features/tasks/stores/taskSessionStore.ts | 7 +- .../src/features/tasks/stores/taskStore.ts | 7 +- apps/mobile/src/features/tasks/types.ts | 103 --------- .../features/tasks/utils/parseSessionLogs.ts | 54 ----- 49 files changed, 774 insertions(+), 929 deletions(-) delete mode 100644 apps/mobile/src/features/inbox/constants.ts delete mode 100644 apps/mobile/src/features/inbox/types.ts delete mode 100644 apps/mobile/src/features/tasks/composer/attachments/cloudPrompt.ts create mode 100644 apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts create mode 100644 apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts delete mode 100644 apps/mobile/src/features/tasks/utils/parseSessionLogs.ts diff --git a/apps/mobile/src/app/(tabs)/inbox.tsx b/apps/mobile/src/app/(tabs)/inbox.tsx index 4013102faa..1e64ab8df4 100644 --- a/apps/mobile/src/app/(tabs)/inbox.tsx +++ b/apps/mobile/src/app/(tabs)/inbox.tsx @@ -1,3 +1,5 @@ +import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering"; +import type { SignalReport } from "@posthog/shared/domain-types"; import { useFocusEffect, useRouter } from "expo-router"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { View } from "react-native"; @@ -20,12 +22,8 @@ import { decidedIds, useDismissedReportsStore, } from "@/features/inbox/stores/dismissedReportsStore"; -import { - DEFAULT_STATUS_FILTER, - useInboxFilterStore, -} from "@/features/inbox/stores/inboxFilterStore"; +import { useInboxFilterStore } from "@/features/inbox/stores/inboxFilterStore"; import { useInboxStore } from "@/features/inbox/stores/inboxStore"; -import type { SignalReport } from "@/features/inbox/types"; import { buildInboxViewedProperties } from "@/features/inbox/utils"; import { useIntegrations } from "@/features/tasks/hooks/useIntegrations"; import { ANALYTICS_EVENTS, useAnalytics } from "@/lib/analytics"; @@ -74,7 +72,7 @@ export default function InboxScreen() { statusFilter, suggestedReviewerFilter, priorityFilter, - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }), ); }, [ diff --git a/apps/mobile/src/app/inbox/[...id].tsx b/apps/mobile/src/app/inbox/[...id].tsx index 48fa3daab2..8ad989bfa9 100644 --- a/apps/mobile/src/app/inbox/[...id].tsx +++ b/apps/mobile/src/app/inbox/[...id].tsx @@ -1,4 +1,16 @@ import { Text } from "@components/text"; +import { + formatSignalReportSummaryMarkdown, + inboxStatusLabel, +} from "@posthog/core/inbox/reportPresentation"; +import { DISMISSAL_REASON_OPTIONS } from "@posthog/shared"; +import type { + ActionabilityJudgmentContent, + SignalFindingContent, + SignalReportPriority, + SignalReportStatus, + SuggestedReviewersArtefact, +} from "@posthog/shared/domain-types"; import { differenceInHours, format, formatDistanceToNow } from "date-fns"; import * as Haptics from "expo-haptics"; import { useLocalSearchParams, useRouter } from "expo-router"; @@ -39,7 +51,6 @@ import { type ReviewerActionExtra, SuggestedReviewers, } from "@/features/inbox/components/SuggestedReviewers"; -import { DISMISSAL_REASON_OPTIONS } from "@/features/inbox/constants"; import { useInboxEngagementTracker } from "@/features/inbox/hooks/useInboxEngagementTracker"; import { useInboxReport, @@ -47,17 +58,6 @@ import { useInboxReportSignals, } from "@/features/inbox/hooks/useInboxReports"; import { useInboxStore } from "@/features/inbox/stores/inboxStore"; -import type { - ActionabilityJudgmentContent, - SignalFindingContent, - SignalReportPriority, - SignalReportStatus, - SuggestedReviewersArtefact, -} from "@/features/inbox/types"; -import { - formatSignalReportSummaryMarkdown, - inboxStatusLabel, -} from "@/features/inbox/utils"; import { PrStatusBadge } from "@/features/tasks/components/PrStatusBadge"; import { computeReportAgeHours, diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index fe73a847f3..31dc3d6e5d 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -1,10 +1,19 @@ import { Text } from "@components/text"; +import { DEFAULT_CLAUDE_EXECUTION_MODE } from "@posthog/core/sessions/executionModes"; import { countUserMessages, getSessionActivityPhase, } from "@posthog/core/sessions/sessionActivity"; import { isTaskRunning } from "@posthog/core/tasks/taskArchive"; -import type { Task } from "@posthog/shared"; +import { + DEFAULT_GATEWAY_MODEL, + DEFAULT_REASONING_EFFORT, + type ExecutionMode, + getReasoningEffortOptions, + type SupportedReasoningEffort, + serializeCloudPrompt, + type Task, +} from "@posthog/shared"; import { useQueryClient } from "@tanstack/react-query"; import * as Haptics from "expo-haptics"; import { useLocalSearchParams, useRouter } from "expo-router"; @@ -20,7 +29,6 @@ import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller import Animated, { useAnimatedStyle } from "react-native-reanimated"; import { FloatingBackButton } from "@/components/FloatingBackButton"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { runTaskInCloud } from "@/features/tasks/api"; import { CustomImageBadge } from "@/features/tasks/components/CustomImageBadge"; import { FloatingTaskHeader } from "@/features/tasks/components/FloatingTaskHeader"; import { PrDiffStatsBadge } from "@/features/tasks/components/PrDiffStatsBadge"; @@ -28,16 +36,7 @@ import { PrStatusBadge } from "@/features/tasks/components/PrStatusBadge"; import { StopRunButton } from "@/features/tasks/components/StopRunButton"; import { TaskSessionView } from "@/features/tasks/components/TaskSessionView"; import { buildCloudPromptBlocks } from "@/features/tasks/composer/attachments/buildCloudPrompt"; -import { serializeCloudPrompt } from "@/features/tasks/composer/attachments/cloudPrompt"; import type { PendingAttachment } from "@/features/tasks/composer/attachments/types"; -import { - DEFAULT_EXECUTION_MODE, - DEFAULT_MODEL, - DEFAULT_REASONING, - type ExecutionMode, - modelSupportsReasoning, - type ReasoningEffort, -} from "@/features/tasks/composer/options"; import { QueuedMessagesDock } from "@/features/tasks/composer/QueuedMessagesDock"; import { TaskChatComposer } from "@/features/tasks/composer/TaskChatComposer"; import { @@ -169,10 +168,10 @@ export default function TaskDetailScreen() { string | undefined >(); const composerMode: ExecutionMode = - composerConfig?.mode ?? DEFAULT_EXECUTION_MODE; - const composerModel = composerConfig?.model ?? DEFAULT_MODEL; - const composerReasoning: ReasoningEffort = - composerConfig?.reasoning ?? DEFAULT_REASONING; + composerConfig?.mode ?? DEFAULT_CLAUDE_EXECUTION_MODE; + const composerModel = composerConfig?.model ?? DEFAULT_GATEWAY_MODEL; + const composerReasoning: SupportedReasoningEffort = + composerConfig?.reasoning ?? DEFAULT_REASONING_EFFORT; const messagingMode = useMessagingMode(taskId); const queuedCount = useQueuedCount(taskId); @@ -315,16 +314,21 @@ export default function TaskDetailScreen() { ) : text; - const supportsReasoning = modelSupportsReasoning(composerModel); - const updatedTask = await runTaskInCloud(taskId, { - resumeFromRunId: task.latest_run?.id, - pendingUserMessage, - runtimeAdapter: "claude", - model: composerModel, - reasoningEffort: supportsReasoning ? composerReasoning : undefined, - initialPermissionMode: composerMode, - rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, - }); + const supportsReasoning = + getReasoningEffortOptions("claude", composerModel) !== null; + const updatedTask = await getPostHogApiClient().runTaskInCloud( + taskId, + undefined, + { + resumeFromRunId: task.latest_run?.id, + pendingUserMessage, + adapter: "claude", + model: composerModel, + reasoningLevel: supportsReasoning ? composerReasoning : undefined, + initialPermissionMode: composerMode, + rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, + }, + ); setTask(updatedTask); await connectToTask(updatedTask); updateTaskInCache(updatedTask); @@ -513,7 +517,7 @@ export default function TaskDetailScreen() { ); const handleReasoningChange = useCallback( - (value: ReasoningEffort) => { + (value: SupportedReasoningEffort) => { if (!taskId) return; setComposerConfig(taskId, { reasoning: value }); setConfigOption(taskId, "effort", value).catch(() => {}); @@ -566,10 +570,14 @@ export default function TaskDetailScreen() { setRetrying(true); disconnectFromTask(taskId); - const updatedTask = await runTaskInCloud(taskId, { - resumeFromRunId: task.latest_run?.id, - rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, - }); + const updatedTask = await getPostHogApiClient().runTaskInCloud( + taskId, + undefined, + { + resumeFromRunId: task.latest_run?.id, + rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, + }, + ); setTask(updatedTask); await connectToTask(updatedTask); updateTaskInCache(updatedTask); diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index 012b206dcc..9df757f9de 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -1,4 +1,17 @@ import { Text } from "@components/text"; +import { + DEFAULT_CLAUDE_EXECUTION_MODE, + getAvailableModes, +} from "@posthog/core/sessions/executionModes"; +import { + DEFAULT_GATEWAY_MODEL, + DEFAULT_REASONING_EFFORT, + type ExecutionMode, + getReasoningEffortOptions, + isSupportedReasoningEffort, + type SupportedReasoningEffort, + serializeCloudPrompt, +} from "@posthog/shared"; import { LinearGradient } from "expo-linear-gradient"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { @@ -15,7 +28,7 @@ import { Sparkle, StopIcon, } from "phosphor-react-native"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { ActivityIndicator, Pressable, @@ -30,13 +43,11 @@ import { import Animated, { runOnJS, useAnimatedStyle } from "react-native-reanimated"; import { useVoiceRecording } from "@/features/chat"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; -import { runTaskInCloud } from "@/features/tasks/api"; import { GitHubConnectionPrompt } from "@/features/tasks/components/GitHubConnectionPrompt"; import { GitHubLoadNotice } from "@/features/tasks/components/GitHubLoadNotice"; import { AttachmentSheet } from "@/features/tasks/composer/attachments/AttachmentSheet"; import { AttachmentsBar } from "@/features/tasks/composer/attachments/AttachmentsBar"; import { buildCloudPromptBlocks } from "@/features/tasks/composer/attachments/buildCloudPrompt"; -import { serializeCloudPrompt } from "@/features/tasks/composer/attachments/cloudPrompt"; import { captureFromCamera, pickDocument, @@ -45,22 +56,15 @@ import { import type { PendingAttachment } from "@/features/tasks/composer/attachments/types"; import { DotBackground } from "@/features/tasks/composer/DotBackground"; import { - DEFAULT_EXECUTION_MODE, - DEFAULT_MODEL, - DEFAULT_REASONING, - EXECUTION_MODES, - type ExecutionMode, - MODELS, - modeLabel, - modelLabel, - modelSupportsReasoning, - REASONING_LEVELS, - type ReasoningEffort, - reasoningLabel, + getMobileModelOptions, + getModelConfigOption, + getModelLabel, + resolveAvailableModel, } from "@/features/tasks/composer/options"; import { Pill } from "@/features/tasks/composer/Pill"; import { RepositoryPickerInline } from "@/features/tasks/composer/RepositoryPickerInline"; import { SelectSheet } from "@/features/tasks/composer/SelectSheet"; +import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions"; import { useUserIntegrations } from "@/features/tasks/hooks/useUserIntegrations"; import { useWarmTask } from "@/features/tasks/hooks/useWarmTask"; import { pendingPromptRecoveryStoreApi } from "@/features/tasks/stores/pendingPromptRecoveryStore"; @@ -84,6 +88,7 @@ import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { toRgba, useThemeColors } from "@/lib/theme"; const log = logger.scope("task-create"); +const EXECUTION_MODES = getAvailableModes(); const SUGGESTIONS = [ "Create or update my CLAUDE.md file", @@ -99,6 +104,11 @@ function modeIcon(mode: ExecutionMode, color: string, size = 14) { return ; case "acceptEdits": return ; + case "bypassPermissions": + case "full-access": + return ; + case "read-only": + return ; case "auto": return ; } @@ -119,6 +129,9 @@ export default function NewTaskScreen() { const { insets, bottom } = useScreenInsets(); const keyboard = useReanimatedKeyboardAnimation(); const restingBottom = bottom("compact"); + const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); + const modelConfigOption = getModelConfigOption(configOptions); + const mobileModelOptions = getMobileModelOptions(modelConfigOption); const { error, hasGithubIntegration, @@ -182,22 +195,32 @@ export default function NewTaskScreen() { const prefs = usePreferencesStore.getState(); if (prefs.defaultInitialTaskMode === "last_used") { const last = prefs.lastNewTaskMode; - const isValidMode = EXECUTION_MODES.some((m) => m.value === last); + const isValidMode = EXECUTION_MODES.some((mode) => mode.id === last); if (isValidMode) return last as ExecutionMode; } - return DEFAULT_EXECUTION_MODE; + return DEFAULT_CLAUDE_EXECUTION_MODE; }); - const [model, setModel] = useState(DEFAULT_MODEL); - const [reasoning, setReasoning] = useState(() => { + const [model, setModel] = useState(DEFAULT_GATEWAY_MODEL); + const [reasoning, setReasoning] = useState(() => { const prefs = usePreferencesStore.getState(); - const isValidReasoning = (v: string): v is ReasoningEffort => - REASONING_LEVELS.some((r) => r.value === v); const desired = prefs.defaultReasoningEffort === "last_used" ? prefs.lastUsedReasoningEffort : prefs.defaultReasoningEffort; - return isValidReasoning(desired) ? desired : DEFAULT_REASONING; + return isSupportedReasoningEffort("claude", DEFAULT_GATEWAY_MODEL, desired) + ? desired + : DEFAULT_REASONING_EFFORT; }); + + useEffect(() => { + if (!hasLiveConfig) return; + const availableModel = resolveAvailableModel(modelConfigOption, model); + if (availableModel === model) return; + setModel(availableModel); + if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) { + setReasoning(DEFAULT_REASONING_EFFORT); + } + }, [hasLiveConfig, model, modelConfigOption, reasoning]); const [creating, setCreating] = useState(false); const [repoSheetOpen, setRepoSheetOpen] = useState(false); const [modeSheetOpen, setModeSheetOpen] = useState(false); @@ -309,7 +332,8 @@ export default function NewTaskScreen() { ? `Attached: ${attachments[0].fileName}` : `Attached ${attachments.length} files`); - const task = await getPostHogApiClient().createTask({ + const client = getPostHogApiClient(); + const task = await client.createTask({ description: descriptionText, title: descriptionText.slice(0, 100), repository: selection.repository ?? undefined, @@ -334,7 +358,7 @@ export default function NewTaskScreen() { // Seed the per-task composer config with the mode/model/reasoning the // user picked here, so the task detail screen reflects them and every // subsequent run (resume-after-terminal) reuses the selected mode rather - // than falling back to DEFAULT_EXECUTION_MODE ("plan"). + // than falling back to the default plan mode. setComposerConfig(task.id, { mode, model, reasoning }); const pendingUserMessage = @@ -344,13 +368,14 @@ export default function NewTaskScreen() { ) : trimmedPrompt; - const supportsReasoning = modelSupportsReasoning(model); + const supportsReasoning = + getReasoningEffortOptions("claude", model) !== null; - await runTaskInCloud(task.id, { + await client.runTaskInCloud(task.id, undefined, { pendingUserMessage, - runtimeAdapter: "claude", + adapter: "claude", model, - reasoningEffort: supportsReasoning ? reasoning : undefined, + reasoningLevel: supportsReasoning ? reasoning : undefined, initialPermissionMode: mode, autoPublish: usePreferencesStore.getState().autoPublishCloudRuns, rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, @@ -386,8 +411,12 @@ export default function NewTaskScreen() { const hasContent = !!prompt.trim() || attachments.length > 0; const canSubmit = - hasContent && isRepositorySelectionComplete(selection) && !creating; - const showReasoningPill = modelSupportsReasoning(model); + hasLiveConfig && + hasContent && + isRepositorySelectionComplete(selection) && + !creating; + const reasoningOptions = getReasoningEffortOptions("claude", model) ?? []; + const showReasoningPill = reasoningOptions.length > 0; // Best-effort prewarm; failures are swallowed. `selection.integrationId` is // the GitHub installation id, not a PostHog integration id — the backend @@ -395,7 +424,7 @@ export default function NewTaskScreen() { useWarmTask({ repository: selection.repository, githubIntegrationId: selection.integrationId, - composerIsEmpty: !hasContent, + composerIsEmpty: !hasContent || !hasLiveConfig, runtimeAdapter: "claude", model, reasoningEffort: showReasoningPill ? reasoning : null, @@ -610,14 +639,17 @@ export default function NewTaskScreen() { ? themeColors.accent[11] : themeColors.gray[11], )} - label={modeLabel(mode)} + label={ + EXECUTION_MODES.find((option) => option.id === mode) + ?.name ?? mode + } accent={mode === "plan"} onPress={() => setModeSheetOpen(true)} /> } - label={modelLabel(model)} + label={getModelLabel(modelConfigOption, model)} onPress={() => setModelSheetOpen(true)} /> @@ -626,7 +658,11 @@ export default function NewTaskScreen() { icon={ } - label={reasoningLabel(reasoning)} + label={ + reasoningOptions.find( + (option) => option.value === reasoning, + )?.name ?? reasoning + } onPress={() => setReasoningSheetOpen(true)} /> ) : null} @@ -715,12 +751,12 @@ export default function NewTaskScreen() { }} onClose={() => setModeSheetOpen(false)} options={EXECUTION_MODES.map((executionMode) => ({ - value: executionMode.value, - label: executionMode.label, + value: executionMode.id, + label: executionMode.name, description: executionMode.description, icon: modeIcon( - executionMode.value, - executionMode.value === "plan" + executionMode.id as ExecutionMode, + executionMode.id === "plan" ? themeColors.accent[11] : themeColors.gray[11], 16, @@ -734,15 +770,16 @@ export default function NewTaskScreen() { value={model} onChange={(value) => { setModel(value); - if (!modelSupportsReasoning(value)) { - setReasoning(DEFAULT_REASONING); + if (!isSupportedReasoningEffort("claude", value, reasoning)) { + setReasoning(DEFAULT_REASONING_EFFORT); } }} onClose={() => setModelSheetOpen(false)} - options={MODELS.map((modelOption) => ({ + options={mobileModelOptions.map((modelOption) => ({ value: modelOption.value, label: modelOption.label, description: modelOption.description, + disabled: modelOption.disabled, icon: , }))} /> @@ -752,14 +789,14 @@ export default function NewTaskScreen() { title="Reasoning" value={reasoning} onChange={(value) => { - const next = value as ReasoningEffort; + const next = value as SupportedReasoningEffort; setReasoning(next); usePreferencesStore.getState().setLastUsedReasoningEffort(next); }} onClose={() => setReasoningSheetOpen(false)} - options={REASONING_LEVELS.map((reasoningLevel) => ({ + options={reasoningOptions.map((reasoningLevel) => ({ value: reasoningLevel.value, - label: reasoningLevel.label, + label: reasoningLevel.name, icon: , }))} /> diff --git a/apps/mobile/src/features/inbox/activityLog.test.ts b/apps/mobile/src/features/inbox/activityLog.test.ts index 8b791296be..8ba0a681fe 100644 --- a/apps/mobile/src/features/inbox/activityLog.test.ts +++ b/apps/mobile/src/features/inbox/activityLog.test.ts @@ -1,3 +1,4 @@ +import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; import { describe, expect, it } from "vitest"; import { attributionLabel, @@ -6,9 +7,8 @@ import { shortSha, taskRunLabel, } from "./activityLog"; -import type { ReportArtefact } from "./types"; -function commit(id: string, createdAt: string): ReportArtefact { +function commit(id: string, createdAt: string): AnySignalReportArtefact { return { id, type: "commit", @@ -22,7 +22,7 @@ function commit(id: string, createdAt: string): ReportArtefact { }; } -function taskRun(id: string, createdAt: string): ReportArtefact { +function taskRun(id: string, createdAt: string): AnySignalReportArtefact { return { id, type: "task_run", @@ -33,13 +33,13 @@ function taskRun(id: string, createdAt: string): ReportArtefact { describe("selectActivityArtefacts", () => { it("keeps only commit and task_run, sorted oldest-first", () => { - const artefacts: ReportArtefact[] = [ + const artefacts: AnySignalReportArtefact[] = [ taskRun("b", "2026-01-02T00:00:00Z"), { id: "x", type: "note", created_at: "2026-01-03T00:00:00Z", - content: {}, + content: { note: "" }, }, commit("a", "2026-01-01T00:00:00Z"), ]; @@ -51,12 +51,12 @@ describe("selectActivityArtefacts", () => { }); it("returns an empty list when there is no activity", () => { - const artefacts: ReportArtefact[] = [ + const artefacts: AnySignalReportArtefact[] = [ { id: "x", type: "note", created_at: "2026-01-01T00:00:00Z", - content: {}, + content: { note: "" }, }, ]; expect(selectActivityArtefacts(artefacts)).toEqual([]); diff --git a/apps/mobile/src/features/inbox/activityLog.ts b/apps/mobile/src/features/inbox/activityLog.ts index 053b84f04b..cb11aefa22 100644 --- a/apps/mobile/src/features/inbox/activityLog.ts +++ b/apps/mobile/src/features/inbox/activityLog.ts @@ -1,12 +1,12 @@ -import type { ReportArtefact } from "./types"; +import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; export type ActivityArtefact = Extract< - ReportArtefact, + AnySignalReportArtefact, { type: "commit" | "task_run" } >; export function selectActivityArtefacts( - artefacts: ReportArtefact[], + artefacts: AnySignalReportArtefact[], ): ActivityArtefact[] { return artefacts .filter( diff --git a/apps/mobile/src/features/inbox/api.ts b/apps/mobile/src/features/inbox/api.ts index 9b37b82725..ed6461ce9f 100644 --- a/apps/mobile/src/features/inbox/api.ts +++ b/apps/mobile/src/features/inbox/api.ts @@ -1,14 +1,9 @@ -import { authedFetch, getBaseUrl, getProjectId, HttpError } from "@/lib/api"; -import { logger } from "@/lib/logger"; -import type { DismissalReasonOptionValue } from "./constants"; - -const log = logger.scope("inbox-api"); - +import type { DismissalReasonOptionValue } from "@posthog/shared"; import type { + AnySignalReportArtefact, AvailableSuggestedReviewer, AvailableSuggestedReviewersResponse, CommitDiffResponse, - ReportArtefact, SignalProcessingStateResponse, SignalReport, SignalReportArtefactsResponse, @@ -16,7 +11,11 @@ import type { SignalReportsQueryParams, SignalReportsResponse, SuggestedReviewerWriteEntry, -} from "./types"; +} from "@posthog/shared/domain-types"; +import { authedFetch, getBaseUrl, getProjectId, HttpError } from "@/lib/api"; +import { logger } from "@/lib/logger"; + +const log = logger.scope("inbox-api"); export async function getSignalReports( params?: SignalReportsQueryParams, @@ -172,7 +171,7 @@ export async function getSignalReportArtefacts( } const data = await response.json(); - const results: ReportArtefact[] = data.results ?? []; + const results: AnySignalReportArtefact[] = data.results ?? []; return { results, count: data.count ?? results.length }; } @@ -245,11 +244,11 @@ export async function getSignalReportSignals( reportId, status: response.status, }); - return { signals: [] }; + return { report: null, signals: [] }; } const data = await response.json(); - return { signals: data.signals ?? [] }; + return { report: data.report ?? null, signals: data.signals ?? [] }; } /** Resolve the repository associated with a signal report via its repo_selection artefact. */ diff --git a/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx b/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx index abe60b8a46..a0aed9bc98 100644 --- a/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx +++ b/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx @@ -1,4 +1,7 @@ import { Text } from "@components/text"; +import { inboxStatusLabel } from "@posthog/core/inbox/reportPresentation"; +import { dismissalReasonLabel } from "@posthog/shared"; +import type { SignalReport } from "@posthog/shared/domain-types"; import * as Haptics from "expo-haptics"; import { ArrowCounterClockwise, Tray } from "phosphor-react-native"; import { memo, useCallback, useEffect, useRef, useState } from "react"; @@ -11,13 +14,7 @@ import { } from "react-native"; import { useThemeColors } from "@/lib/theme"; import { useArchivedReports, useRestoreReport } from "../hooks/useInboxReports"; -import type { SignalReport } from "../types"; -import { - dismissalReasonLabel, - formatReportTimestamp, - inboxStatusLabel, - isRestorableReport, -} from "../utils"; +import { formatReportTimestamp, isRestorableReport } from "../utils"; interface ArchivedReportListProps { onReportPress?: (report: SignalReport) => void; diff --git a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx index fb176374a6..19f4cc2510 100644 --- a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx +++ b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; +import type { CommitContent } from "@posthog/shared/domain-types"; import { CaretDown, CaretRight } from "phosphor-react-native"; import { useState } from "react"; import { ActivityIndicator, Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; import { shortSha } from "../activityLog"; import { useCommitDiff } from "../hooks/useInboxReports"; -import type { CommitContent } from "../types"; import { DiffBlock } from "./DiffBlock"; export function ArtefactCommit({ diff --git a/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx b/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx index bea2436c62..2c2b004a58 100644 --- a/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx +++ b/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; +import type { TaskRunArtefactContent } from "@posthog/shared/domain-types"; import { useRouter } from "expo-router"; import { CaretRight } from "phosphor-react-native"; import { Pressable, View } from "react-native"; import { useTask } from "@/features/tasks"; import { useThemeColors } from "@/lib/theme"; import { taskRunLabel } from "../activityLog"; -import type { TaskRunArtefactContent } from "../types"; export function ArtefactTaskRun({ content, diff --git a/apps/mobile/src/features/inbox/components/DismissReportSheet.tsx b/apps/mobile/src/features/inbox/components/DismissReportSheet.tsx index 65fc913399..56253e4a2c 100644 --- a/apps/mobile/src/features/inbox/components/DismissReportSheet.tsx +++ b/apps/mobile/src/features/inbox/components/DismissReportSheet.tsx @@ -1,4 +1,8 @@ import { Text } from "@components/text"; +import { + DISMISSAL_REASON_OPTIONS, + type DismissalReasonOptionValue, +} from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { Check } from "phosphor-react-native"; import { useEffect, useState } from "react"; @@ -14,10 +18,6 @@ import { } from "react-native"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; -import { - DISMISSAL_REASON_OPTIONS, - type DismissalReasonOptionValue, -} from "../constants"; import { useDismissReport } from "../hooks/useInboxReports"; export interface DismissReportResult { diff --git a/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx b/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx index 2e30695576..ad1d1e013c 100644 --- a/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx +++ b/apps/mobile/src/features/inbox/components/EditReviewersSheet.tsx @@ -3,6 +3,10 @@ import { buildReviewerOptions, reviewerMatchesAvailable, } from "@posthog/core/inbox/artefacts"; +import type { + AvailableSuggestedReviewer, + SuggestedReviewer, +} from "@posthog/shared/domain-types"; import { MagnifyingGlass } from "phosphor-react-native"; import { useMemo, useState } from "react"; import { @@ -17,7 +21,6 @@ import { useDebouncedValue } from "@/hooks/useDebouncedValue"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; import { useAvailableSuggestedReviewers } from "../hooks/useInboxReports"; -import type { AvailableSuggestedReviewer, SuggestedReviewer } from "../types"; import { ReviewerOptionRow } from "./ReviewerOptionRow"; interface EditReviewersSheetProps { diff --git a/apps/mobile/src/features/inbox/components/FilterSheet.tsx b/apps/mobile/src/features/inbox/components/FilterSheet.tsx index 11ed58e5af..47a22b36d8 100644 --- a/apps/mobile/src/features/inbox/components/FilterSheet.tsx +++ b/apps/mobile/src/features/inbox/components/FilterSheet.tsx @@ -1,15 +1,13 @@ import { Text } from "@components/text"; -import { EXTERNAL_INBOX_SOURCES } from "@posthog/shared"; +import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering"; +import { inboxStatusLabel } from "@posthog/core/inbox/reportPresentation"; +import { EXTERNAL_INBOX_SOURCES, type SourceProduct } from "@posthog/shared"; +import type { SignalReportPriority } from "@posthog/shared/domain-types"; import { Check } from "phosphor-react-native"; import { Modal, Pressable, ScrollView, View } from "react-native"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; -import { - type SourceProduct, - useInboxFilterStore, -} from "../stores/inboxFilterStore"; -import type { SignalReportPriority, SignalReportStatus } from "../types"; -import { inboxStatusLabel } from "../utils"; +import { useInboxFilterStore } from "../stores/inboxFilterStore"; interface FilterSheetProps { visible: boolean; @@ -29,15 +27,6 @@ const SORT_OPTIONS: SortOption[] = [ { label: "Oldest first", field: "created_at", direction: "asc" }, ]; -const FILTERABLE_STATUSES: SignalReportStatus[] = [ - "ready", - "pending_input", - "in_progress", - "failed", - "candidate", - "potential", -]; - function useStatusDotColors(): Record { const themeColors = useThemeColors(); return { @@ -74,9 +63,11 @@ export const SOURCE_PRODUCT_OPTIONS: { value: SourceProduct; label: string }[] = { value: "session_replay", label: "Session replay" }, { value: "error_tracking", label: "Error tracking" }, { value: "llm_analytics", label: "AI observability" }, + { value: "github", label: "GitHub" }, + { value: "linear", label: "Linear" }, + { value: "zendesk", label: "Zendesk" }, { value: "conversations", label: "Conversations" }, { value: "signals_scout", label: "Scout" }, - { value: "health_checks", label: "Health checks" }, ...EXTERNAL_INBOX_SOURCES.map((source) => ({ value: source.product, label: source.label, @@ -141,7 +132,7 @@ export function FilterSheet({ visible, onClose }: FilterSheetProps) { const hasActiveFilters = sourceProductFilter.length > 0 || priorityFilter.length > 0 || - statusFilter.length < FILTERABLE_STATUSES.length; + statusFilter.length < INBOX_PIPELINE_STATUSES.length; return ( - {FILTERABLE_STATUSES.map((status) => ( + {INBOX_PIPELINE_STATUSES.map((status) => ( s.currentIndex); @@ -240,7 +245,8 @@ export function TinderView({ // 3. Create the task const prompt = `Act on this signal report. Investigate the root cause, implement the fix, and open a PR if appropriate.\n\n${report.summary ?? ""}`; - const task = await getPostHogApiClient().createTask({ + const client = getPostHogApiClient(); + const task = await client.createTask({ description: prompt, title: prompt.slice(0, 255), repository: match?.repository ?? repo ?? undefined, @@ -251,10 +257,10 @@ export function TinderView({ } as CreateTaskOptions); // 4. Run it - await runTaskInCloud(task.id, { + await client.runTaskInCloud(task.id, undefined, { pendingUserMessage: prompt, - runtimeAdapter: "claude", - model: DEFAULT_MODEL, + adapter: "claude", + model, initialPermissionMode: "plan", runSource: "signal_report", signalReportId: report.id, @@ -276,6 +282,7 @@ export function TinderView({ }, [ repositoryOptions, + model, showToastPending, showToastDone, acceptReport, @@ -489,7 +496,7 @@ export function TinderView({ setExpandedReport(null); }} className="h-16 w-16 items-center justify-center rounded-full border-2 border-status-success bg-status-success/10 active:bg-status-success/20" - disabled={creating} + disabled={creating || !hasLiveConfig} hitSlop={8} > {creating ? ( diff --git a/apps/mobile/src/features/inbox/constants.ts b/apps/mobile/src/features/inbox/constants.ts deleted file mode 100644 index f15aca7c5b..0000000000 --- a/apps/mobile/src/features/inbox/constants.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Reasons offered when the user dismisses a signal report. - * Mirrors apps/code/src/shared/dismissalReasons.ts. - */ -export const DISMISSAL_REASON_OPTIONS = [ - { - value: "already_fixed", - label: "Already fixed", - snoozesInsteadOfDismiss: true, - }, - { value: "report_unclear", label: "Report is unclear to me" }, - { value: "analysis_wrong", label: "Agent's analysis is wrong" }, - { value: "wontfix_intentional", label: "Won't fix — intentional behavior" }, - { - value: "wontfix_irrelevant", - label: "Won't fix — issue is real but insignificant", - }, - { value: "other", label: "Something else…" }, -] as const; - -export type DismissalReasonOptionValue = - (typeof DISMISSAL_REASON_OPTIONS)[number]["value"]; diff --git a/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.test.ts b/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.test.ts index 2e970def6b..16cd3b1f64 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.test.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.test.ts @@ -9,8 +9,8 @@ vi.mock("posthog-react-native", () => ({ usePostHog: () => null, })); +import type { SignalReport } from "@posthog/shared/domain-types"; import { ANALYTICS_EVENTS, type Analytics } from "@/lib/analytics"; -import type { SignalReport } from "../types"; import { type InboxEngagementTracker, type UseInboxEngagementTrackerOptions, diff --git a/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.ts b/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.ts index 67ac03e03a..323f03a938 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxEngagementTracker.ts @@ -1,3 +1,4 @@ +import type { SignalReport } from "@posthog/shared/domain-types"; import { useCallback, useEffect, useRef } from "react"; import { ANALYTICS_EVENTS, @@ -7,7 +8,6 @@ import { type InboxReportCloseMethod, type InboxReportOpenMethod, } from "@/lib/analytics"; -import type { SignalReport } from "../types"; interface OpenInfo { reportId: string; diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts index 407730a054..ad4abf8190 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts @@ -1,3 +1,7 @@ +import type { + SignalReport, + SignalReportsResponse, +} from "@posthog/shared/domain-types"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createElement } from "react"; import { act, create } from "react-test-renderer"; @@ -16,7 +20,6 @@ vi.mock("../api", () => ({ getAvailableSuggestedReviewers(query), })); -import type { SignalReport, SignalReportsResponse } from "../types"; import { getReportsNextPageParam, useAvailableSuggestedReviewers, diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts index ac37c738d1..a2d91f0520 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts @@ -7,6 +7,19 @@ import { INBOX_DISMISSED_STATUS_FILTER, INBOX_REFETCH_INTERVAL_MS, } from "@posthog/core/inbox/reportFiltering"; +import type { + AvailableSuggestedReviewersResponse, + CommitDiffResponse, + SignalProcessingStateResponse, + SignalReport, + SignalReportArtefactsResponse, + SignalReportSignalsResponse, + SignalReportsQueryParams, + SignalReportsResponse, + SuggestedReviewer, + SuggestedReviewersArtefact, + SuggestedReviewerWriteEntry, +} from "@posthog/shared/domain-types"; import { useInfiniteQuery, useMutation, @@ -29,18 +42,6 @@ import { updateSignalReportArtefact, } from "../api"; import { useInboxFilterStore } from "../stores/inboxFilterStore"; -import type { - AvailableSuggestedReviewersResponse, - CommitDiffResponse, - SignalProcessingStateResponse, - SignalReport, - SignalReportArtefactsResponse, - SignalReportSignalsResponse, - SignalReportsQueryParams, - SignalReportsResponse, - SuggestedReviewer, - SuggestedReviewerWriteEntry, -} from "../types"; import { isRestorableReport } from "../utils"; export const inboxKeys = { @@ -264,12 +265,20 @@ export function useUpdateSuggestedReviewers(reportId: string) { if (previous) { queryClient.setQueryData(queryKey, { ...previous, - results: previous.results.map((artefact) => - artefact.id === artefactId && - artefact.type === "suggested_reviewers" - ? { ...artefact, content: optimisticReviewers } - : artefact, - ), + results: previous.results.map((artefact) => { + if ( + artefact.id === artefactId && + artefact.type === "suggested_reviewers" + ) { + const updatedArtefact: SuggestedReviewersArtefact = { + ...artefact, + type: "suggested_reviewers", + content: optimisticReviewers, + }; + return updatedArtefact; + } + return artefact; + }), }); } return { previous }; diff --git a/apps/mobile/src/features/inbox/stores/inboxFilterStore.test.ts b/apps/mobile/src/features/inbox/stores/inboxFilterStore.test.ts index 935407c640..3bfc12271b 100644 --- a/apps/mobile/src/features/inbox/stores/inboxFilterStore.test.ts +++ b/apps/mobile/src/features/inbox/stores/inboxFilterStore.test.ts @@ -1,3 +1,4 @@ +import type { SourceProduct } from "@posthog/shared"; import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@react-native-async-storage/async-storage", () => ({ @@ -8,7 +9,7 @@ vi.mock("@react-native-async-storage/async-storage", () => ({ }, })); -import { type SourceProduct, useInboxFilterStore } from "./inboxFilterStore"; +import { useInboxFilterStore } from "./inboxFilterStore"; describe("inboxFilterStore", () => { beforeEach(() => { diff --git a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts index a0536417da..1f779d7dd7 100644 --- a/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts +++ b/apps/mobile/src/features/inbox/stores/inboxFilterStore.ts @@ -1,13 +1,13 @@ import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering"; import type { SourceProduct } from "@posthog/shared"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { create } from "zustand"; -import { createJSONStorage, persist } from "zustand/middleware"; import type { SignalReportOrderingField, SignalReportPriority, SignalReportStatus, -} from "../types"; +} from "@posthog/shared/domain-types"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { create } from "zustand"; +import { createJSONStorage, persist } from "zustand/middleware"; type SortField = Extract< SignalReportOrderingField, @@ -16,12 +16,6 @@ type SortField = Extract< type SortDirection = "asc" | "desc"; -export type { SourceProduct }; - -export const DEFAULT_STATUS_FILTER: SignalReportStatus[] = [ - ...INBOX_PIPELINE_STATUSES, -]; - interface InboxFilterState { sortField: SortField; sortDirection: SortDirection; @@ -51,7 +45,7 @@ export const useInboxFilterStore = create()( (set) => ({ sortField: "priority", sortDirection: "asc", - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: [...INBOX_PIPELINE_STATUSES], sourceProductFilter: [], suggestedReviewerFilter: [], priorityFilter: [], @@ -100,7 +94,7 @@ export const useInboxFilterStore = create()( set({ priorityFilter: Array.from(new Set(priorities)) }), resetFilters: () => set({ - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: [...INBOX_PIPELINE_STATUSES], sourceProductFilter: [], suggestedReviewerFilter: [], priorityFilter: [], diff --git a/apps/mobile/src/features/inbox/stores/inboxStore.ts b/apps/mobile/src/features/inbox/stores/inboxStore.ts index 7bdb2ec206..32120c07a2 100644 --- a/apps/mobile/src/features/inbox/stores/inboxStore.ts +++ b/apps/mobile/src/features/inbox/stores/inboxStore.ts @@ -1,5 +1,5 @@ +import type { SignalReportOrderingField } from "@posthog/shared/domain-types"; import { create } from "zustand"; -import type { SignalReportOrderingField } from "../types"; type OrderDirection = "asc" | "desc"; diff --git a/apps/mobile/src/features/inbox/types.ts b/apps/mobile/src/features/inbox/types.ts deleted file mode 100644 index 248ab5f86b..0000000000 --- a/apps/mobile/src/features/inbox/types.ts +++ /dev/null @@ -1,209 +0,0 @@ -import type { DismissalReasonOptionValue } from "./constants"; - -export type SignalReportStatus = - | "potential" - | "candidate" - | "in_progress" - | "ready" - | "failed" - | "pending_input" - | "resolved" - | "suppressed" - | "deleted"; - -export type SignalReportPriority = "P0" | "P1" | "P2" | "P3" | "P4"; - -export type SignalReportActionability = - | "immediately_actionable" - | "requires_human_input" - | "not_actionable"; - -export interface SignalReport { - id: string; - title: string | null; - summary: string | null; - status: SignalReportStatus; - total_weight: number; - signal_count: number; - signals_at_run?: number; - created_at: string; - updated_at: string; - artefact_count: number; - priority?: SignalReportPriority | null; - actionability?: SignalReportActionability | null; - already_addressed?: boolean | null; - dismissal_reason?: DismissalReasonOptionValue | null; - dismissal_note?: string | null; - is_suggested_reviewer?: boolean; - source_products?: string[]; - implementation_pr_url?: string | null; -} - -export interface SignalReportsResponse { - results: SignalReport[]; - count: number; -} - -export type SignalReportOrderingField = - | "priority" - | "signal_count" - | "total_weight" - | "created_at" - | "updated_at"; - -export interface SignalReportsQueryParams { - limit?: number; - offset?: number; - status?: string; - ordering?: string; - source_product?: string; - suggested_reviewers?: string; - priority?: string; -} - -export interface SignalProcessingStateResponse { - paused_until: string | null; -} - -export interface AvailableSuggestedReviewer { - uuid: string; - name: string; - email: string; - github_login: string; -} - -export interface AvailableSuggestedReviewersResponse { - results: AvailableSuggestedReviewer[]; - count: number; -} - -export interface Signal { - signal_id: string; - content: string; - source_product: string; - source_type: string; - source_id: string; - weight: number; - timestamp: string; - extra: Record; -} - -export interface SignalFindingContent { - signal_id: string; - relevant_code_paths: string[]; - relevant_commit_hashes: Record; - data_queried: string; - verified: boolean; -} - -export interface PriorityJudgmentContent { - explanation: string; - priority: SignalReportPriority; -} - -export interface ActionabilityJudgmentContent { - explanation: string; - actionability: SignalReportActionability; - already_addressed: boolean; -} - -export interface SuggestedReviewerCommit { - sha: string; - url: string; - reason: string; -} - -export interface SuggestedReviewerUser { - id: number; - uuid: string; - email: string; - first_name: string; - last_name: string; -} - -export interface SuggestedReviewer { - github_login: string; - github_name: string | null; - relevant_commits: SuggestedReviewerCommit[]; - user: SuggestedReviewerUser | null; -} - -export interface SuggestedReviewersArtefact { - id: string; - type: "suggested_reviewers"; - created_at: string; - content: SuggestedReviewer[]; -} - -/** - * Write shape for replacing the suggested_reviewers artefact. The server - * canonicalizes to a lowercase `github_login`, with `user_uuid` winning when - * both are supplied. - */ -export interface SuggestedReviewerWriteEntry { - github_login?: string; - user_uuid?: string; - github_name?: string; -} - -export interface ArtefactUser { - uuid?: string; - email: string; - first_name?: string; - last_name?: string; -} - -export interface CommitContent { - repository: string; - branch: string; - commit_sha: string; - message: string; - note?: string | null; -} - -export interface TaskRunArtefactContent { - task_id: string; - product: string; - type: string; -} - -export interface CommitDiffResponse { - diff: string; - truncated: boolean; -} - -/** - * Fields shared by every artefact row. `created_by` / `task_id` carry - * attribution: at most one is set — `created_by` for user writes, `task_id` - * for agent writes, neither for system writes. - */ -interface BaseArtefact { - id: string; - created_at: string; - created_by?: ArtefactUser | null; - task_id?: string | null; -} - -export type ReportArtefact = - | (BaseArtefact & { - type: "priority_judgment"; - content: PriorityJudgmentContent; - }) - | (BaseArtefact & { - type: "actionability_judgment"; - content: ActionabilityJudgmentContent; - }) - | (BaseArtefact & { type: "signal_finding"; content: SignalFindingContent }) - | (BaseArtefact & { type: "commit"; content: CommitContent }) - | (BaseArtefact & { type: "task_run"; content: TaskRunArtefactContent }) - | (BaseArtefact & SuggestedReviewersArtefact) - | (BaseArtefact & { type: string; content: unknown }); - -export interface SignalReportArtefactsResponse { - results: ReportArtefact[]; - count: number; -} - -export interface SignalReportSignalsResponse { - signals: Signal[]; -} diff --git a/apps/mobile/src/features/inbox/utils.test.ts b/apps/mobile/src/features/inbox/utils.test.ts index ad56b19305..ce8b2b43af 100644 --- a/apps/mobile/src/features/inbox/utils.test.ts +++ b/apps/mobile/src/features/inbox/utils.test.ts @@ -1,9 +1,20 @@ +import { + buildArchiveListOrdering, + buildPriorityFilterParam, + buildSignalReportListOrdering, + INBOX_PIPELINE_STATUSES, +} from "@posthog/core/inbox/reportFiltering"; +import { formatSignalReportSummaryMarkdown } from "@posthog/core/inbox/reportPresentation"; +import { dismissalReasonLabel } from "@posthog/shared"; +import type { + Signal, + SignalReport, + SignalReportOrderingField, + SignalReportStatus, +} from "@posthog/shared/domain-types"; import { describe, expect, it } from "vitest"; -import type { Signal, SignalReport, SignalReportStatus } from "./types"; import { buildInboxViewedProperties, - dismissalReasonLabel, - formatSignalReportSummaryMarkdown, isRestorableReport, sourceLine, } from "./utils"; @@ -21,15 +32,6 @@ function signal(source_product: string, source_type: string): Signal { }; } -const DEFAULT_STATUS_FILTER: SignalReportStatus[] = [ - "ready", - "pending_input", - "in_progress", - "failed", - "candidate", - "potential", -]; - function makeReport( partial: Partial & Pick, ): SignalReport { @@ -88,10 +90,10 @@ describe("buildInboxViewedProperties", () => { it("emits zero counts for an empty list", () => { const props = buildInboxViewedProperties([], 0, { sourceProductFilter: [], - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(props).toMatchObject({ report_count: 0, @@ -137,10 +139,10 @@ describe("buildInboxViewedProperties", () => { const props = buildInboxViewedProperties(reports, 4, { sourceProductFilter: [], - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(props.report_count).toBe(4); @@ -161,36 +163,36 @@ describe("buildInboxViewedProperties", () => { statusFilter: ["ready"], suggestedReviewerFilter: [], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(narrowed.has_active_filters).toBe(true); expect(narrowed.status_filter_count).toBe(1); const sourced = buildInboxViewedProperties([], 0, { sourceProductFilter: ["error_tracking"], - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(sourced.has_active_filters).toBe(true); expect(sourced.source_product_filter).toEqual(["error_tracking"]); const reviewer = buildInboxViewedProperties([], 0, { sourceProductFilter: [], - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: ["uuid-1"], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(reviewer.has_active_filters).toBe(true); const prioritized = buildInboxViewedProperties([], 0, { sourceProductFilter: [], - statusFilter: DEFAULT_STATUS_FILTER, + statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], priorityFilter: ["P0"], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(prioritized.has_active_filters).toBe(true); }); @@ -198,15 +200,89 @@ describe("buildInboxViewedProperties", () => { it("treats a reordered default status set as not filtered", () => { const props = buildInboxViewedProperties([], 0, { sourceProductFilter: [], - statusFilter: [...DEFAULT_STATUS_FILTER].reverse(), + statusFilter: [...INBOX_PIPELINE_STATUSES].reverse(), suggestedReviewerFilter: [], priorityFilter: [], - defaultStatusFilter: DEFAULT_STATUS_FILTER, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, }); expect(props.has_active_filters).toBe(false); }); }); +describe("buildSignalReportListOrdering", () => { + it.each([ + { + field: "priority" as SignalReportOrderingField, + direction: "desc" as const, + expected: "status,-priority,-created_at", + }, + { + field: "priority" as SignalReportOrderingField, + direction: "asc" as const, + expected: "status,priority,-created_at", + }, + { + field: "signal_count" as SignalReportOrderingField, + direction: "desc" as const, + expected: "status,-signal_count,priority", + }, + { + field: "total_weight" as SignalReportOrderingField, + direction: "asc" as const, + expected: "status,total_weight,priority", + }, + { + field: "created_at" as SignalReportOrderingField, + direction: "desc" as const, + expected: "status,-created_at,priority", + }, + { + field: "updated_at" as SignalReportOrderingField, + direction: "asc" as const, + expected: "status,updated_at,priority", + }, + ])( + "orders $field $direction as $expected", + ({ field, direction, expected }) => { + expect(buildSignalReportListOrdering(field, direction)).toBe(expected); + }, + ); +}); + +describe("buildPriorityFilterParam", () => { + it.each([ + { + name: "returns undefined for an empty selection", + input: [], + expected: undefined, + }, + { + name: "joins selected priorities with commas", + input: ["P0", "P2"] as const, + expected: "P0,P2", + }, + { + name: "dedupes repeated priorities", + input: ["P1", "P1", "P3"] as const, + expected: "P1,P3", + }, + ])("$name", ({ input, expected }) => { + expect(buildPriorityFilterParam([...input])).toBe(expected); + }); +}); + +describe("buildArchiveListOrdering", () => { + it.each([ + { direction: "desc" as const, expected: "-updated_at" }, + { direction: "asc" as const, expected: "updated_at" }, + ])( + "sorts by field without a status prefix ($direction)", + ({ direction, expected }) => { + expect(buildArchiveListOrdering("updated_at", direction)).toBe(expected); + }, + ); +}); + describe("isRestorableReport", () => { it.each([ { status: "suppressed" as SignalReportStatus, expected: true }, diff --git a/apps/mobile/src/features/inbox/utils.ts b/apps/mobile/src/features/inbox/utils.ts index b0040ddcef..13a8e4c80b 100644 --- a/apps/mobile/src/features/inbox/utils.ts +++ b/apps/mobile/src/features/inbox/utils.ts @@ -2,15 +2,14 @@ import { EXTERNAL_INBOX_SOURCE_BY_PRODUCT, type SourceProduct, } from "@posthog/shared"; -import { differenceInHours, format, formatDistanceToNow } from "date-fns"; -import type { InboxViewedProperties } from "@/lib/analytics"; -import { DISMISSAL_REASON_OPTIONS } from "./constants"; import type { Signal, SignalReport, SignalReportPriority, SignalReportStatus, -} from "./types"; +} from "@posthog/shared/domain-types"; +import { differenceInHours, format, formatDistanceToNow } from "date-fns"; +import type { InboxViewedProperties } from "@/lib/analytics"; const ERROR_TRACKING_TYPE_LABELS: Record = { issue_created: "New issue", @@ -46,34 +45,7 @@ export function sourceLine(signal: Signal): string { const warehouseSource = EXTERNAL_INBOX_SOURCE_BY_PRODUCT[source_product as SourceProduct]; const product = warehouseSource?.label ?? source_product.replace(/_/g, " "); - const type = source_type.replace(/_/g, " "); - return `${product} · ${type}`; -} - -const SIGNAL_SUMMARY_SECTION_HEADERS = [ - "What's happening", - "Root cause", - "How to resolve", -] as const; - -/** - * Inserts blank lines around signal report summary section headers so each - * label and its body render on their own line (agent output often packs them - * together, e.g. `**What's happening:** text **Root cause:** ...`). - */ -export function formatSignalReportSummaryMarkdown(content: string): string { - let result = content; - - for (const header of SIGNAL_SUMMARY_SECTION_HEADERS) { - const boldHeader = `\\*\\*${header}:\\*\\*`; - result = result.replace( - new RegExp(`([^\\n])\\s*(${boldHeader})`, "gi"), - "$1\n\n$2", - ); - result = result.replace(new RegExp(`(${boldHeader})\\s+`, "gi"), "$1\n\n"); - } - - return result; + return `${product} · ${source_type.replace(/_/g, " ")}`; } /** Relative time for the last day, absolute "MMM d" beyond it. */ @@ -93,38 +65,6 @@ export function isRestorableReport( return report.status === "suppressed"; } -/** Human label for a persisted dismissal reason, falling back to the raw code. */ -export function dismissalReasonLabel(value: string): string { - return ( - DISMISSAL_REASON_OPTIONS.find((o) => o.value === value)?.label ?? value - ); -} - -export function inboxStatusLabel(status: SignalReportStatus): string { - switch (status) { - case "ready": - return "Ready"; - case "resolved": - return "Resolved"; - case "pending_input": - return "Needs input"; - case "in_progress": - return "Researching"; - case "candidate": - return "Queued"; - case "potential": - return "Gathering"; - case "failed": - return "Failed"; - case "suppressed": - return "Suppressed"; - case "deleted": - return "Deleted"; - default: - return status; - } -} - /** * Returns only reports that are actionable for the tinder-like card deck: * ready, immediately actionable, not already addressed. @@ -140,11 +80,11 @@ export function getActionableReports(reports: SignalReport[]): SignalReport[] { interface InboxViewedFilterState { sourceProductFilter: string[]; - statusFilter: SignalReportStatus[]; + statusFilter: readonly SignalReportStatus[]; suggestedReviewerFilter: string[]; priorityFilter: SignalReportPriority[]; /** Default status filter as defined in the filter store, used to detect whether the user has narrowed it. */ - defaultStatusFilter: SignalReportStatus[]; + defaultStatusFilter: readonly SignalReportStatus[]; } /** diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index 4297268370..b61e6f732e 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -1,4 +1,16 @@ import { Text } from "@components/text"; +import { + DEFAULT_CLAUDE_EXECUTION_MODE, + getAvailableModes, +} from "@posthog/core/sessions/executionModes"; +import { + DEFAULT_GATEWAY_MODEL, + DEFAULT_REASONING_EFFORT, + type ExecutionMode, + getReasoningEffortOptions, + isSupportedReasoningEffort, + type SupportedReasoningEffort, +} from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { ArrowUp, @@ -32,6 +44,7 @@ import { View, } from "react-native"; import { useVoiceRecording } from "@/features/chat"; +import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions"; import { logger } from "@/lib/logger"; import { useThemeColors } from "@/lib/theme"; import type { MessagingMode } from "../stores/messagingModeStore"; @@ -44,18 +57,10 @@ import { } from "./attachments/pickers"; import type { PendingAttachment } from "./attachments/types"; import { - DEFAULT_EXECUTION_MODE, - DEFAULT_MODEL, - DEFAULT_REASONING, - EXECUTION_MODES, - type ExecutionMode, - MODELS, - modeLabel, - modelLabel, - modelSupportsReasoning, - REASONING_LEVELS, - type ReasoningEffort, - reasoningLabel, + getMobileModelOptions, + getModelConfigOption, + getModelLabel, + resolveAvailableModel, } from "./options"; import { Pill } from "./Pill"; import { SelectSheet } from "./SelectSheet"; @@ -66,6 +71,7 @@ import { } from "./submitComposerMessage"; const log = logger.scope("task-chat-composer"); +const EXECUTION_MODES = getAvailableModes(); interface TaskChatComposerProps { onSend: ( @@ -80,10 +86,10 @@ interface TaskChatComposerProps { /** Current pill values (persisted per-task by the caller). */ mode: ExecutionMode; model: string; - reasoning: ReasoningEffort; + reasoning: SupportedReasoningEffort; onModeChange: (mode: ExecutionMode) => void; onModelChange: (model: string) => void; - onReasoningChange: (reasoning: ReasoningEffort) => void; + onReasoningChange: (reasoning: SupportedReasoningEffort) => void; /** Steer vs Queue behaviour for messages sent while a turn is running. */ messagingMode: MessagingMode; queuedCount: number; @@ -103,6 +109,11 @@ function modeIcon(mode: ExecutionMode, color: string, size = 14): ReactNode { return ; case "acceptEdits": return ; + case "bypassPermissions": + case "full-access": + return ; + case "read-only": + return ; case "auto": return ; } @@ -183,6 +194,9 @@ export function TaskChatComposer({ onCancelEdit, }: TaskChatComposerProps) { const themeColors = useThemeColors(); + const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); + const modelConfigOption = getModelConfigOption(configOptions); + const mobileModelOptions = getMobileModelOptions(modelConfigOption); const [message, setMessage] = useState(() => initialMessage ?? ""); const [attachments, setAttachments] = useState([]); const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false); @@ -206,6 +220,23 @@ export function TaskChatComposer({ setAttachments(restoredDraft.attachments); }, [restoredDraft]); + useEffect(() => { + if (!hasLiveConfig) return; + const availableModel = resolveAvailableModel(modelConfigOption, model); + if (availableModel === model) return; + onModelChange(availableModel); + if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) { + onReasoningChange(DEFAULT_REASONING_EFFORT); + } + }, [ + hasLiveConfig, + model, + modelConfigOption, + onModelChange, + onReasoningChange, + reasoning, + ]); + const appendTranscript = useCallback((transcript: string) => { setMessage((prev) => (prev ? `${prev} ${transcript}` : transcript)); }, []); @@ -220,7 +251,8 @@ export function TaskChatComposer({ const [modelSheetOpen, setModelSheetOpen] = useState(false); const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); - const showReasoningPill = modelSupportsReasoning(model); + const reasoningOptions = getReasoningEffortOptions("claude", model) ?? []; + const showReasoningPill = reasoningOptions.length > 0; const hasContent = !isComposerEmpty({ text: message, attachments }); const canSend = hasContent && !disabled && !isRecording; @@ -399,21 +431,28 @@ export function TaskChatComposer({ ? themeColors.accent[11] : themeColors.gray[11], )} - label={modeLabel(mode)} + label={ + EXECUTION_MODES.find((option) => option.id === mode) + ?.name ?? mode + } accent={mode === "plan"} onPress={() => setModeSheetOpen(true)} /> } - label={modelLabel(model)} + label={getModelLabel(modelConfigOption, model)} onPress={() => setModelSheetOpen(true)} /> {showReasoningPill ? ( } - label={reasoningLabel(reasoning)} + label={ + reasoningOptions.find( + (option) => option.value === reasoning, + )?.name ?? reasoning + } onPress={() => setReasoningSheetOpen(true)} /> ) : null} @@ -462,12 +501,12 @@ export function TaskChatComposer({ onChange={(v) => onModeChange(v as ExecutionMode)} onClose={() => setModeSheetOpen(false)} options={EXECUTION_MODES.map((m) => ({ - value: m.value, - label: m.label, + value: m.id, + label: m.name, description: m.description, icon: modeIcon( - m.value, - m.value === "plan" ? themeColors.accent[11] : themeColors.gray[11], + m.id as ExecutionMode, + m.id === "plan" ? themeColors.accent[11] : themeColors.gray[11], 16, ), }))} @@ -479,18 +518,16 @@ export function TaskChatComposer({ value={model} onChange={(v) => { onModelChange(v); - // If the new model doesn't support reasoning, drop the level so the - // payload stays consistent. Default reasoning re-applies when - // switching back to a reasoning-capable model. - if (!modelSupportsReasoning(v)) { - onReasoningChange(DEFAULT_REASONING); + if (!isSupportedReasoningEffort("claude", v, reasoning)) { + onReasoningChange(DEFAULT_REASONING_EFFORT); } }} onClose={() => setModelSheetOpen(false)} - options={MODELS.map((m) => ({ + options={mobileModelOptions.map((m) => ({ value: m.value, label: m.label, description: m.description, + disabled: m.disabled, icon: , }))} /> @@ -499,11 +536,11 @@ export function TaskChatComposer({ open={reasoningSheetOpen} title="Reasoning" value={reasoning} - onChange={(v) => onReasoningChange(v as ReasoningEffort)} + onChange={(v) => onReasoningChange(v as SupportedReasoningEffort)} onClose={() => setReasoningSheetOpen(false)} - options={REASONING_LEVELS.map((r) => ({ + options={reasoningOptions.map((r) => ({ value: r.value, - label: r.label, + label: r.name, icon: , }))} /> @@ -520,7 +557,7 @@ export function TaskChatComposer({ } export const TASK_CHAT_DEFAULTS = { - mode: DEFAULT_EXECUTION_MODE, - model: DEFAULT_MODEL, - reasoning: DEFAULT_REASONING, + mode: DEFAULT_CLAUDE_EXECUTION_MODE, + model: DEFAULT_GATEWAY_MODEL, + reasoning: DEFAULT_REASONING_EFFORT, } as const; diff --git a/apps/mobile/src/features/tasks/composer/attachments/cloudPrompt.ts b/apps/mobile/src/features/tasks/composer/attachments/cloudPrompt.ts deleted file mode 100644 index 35f885cdc7..0000000000 --- a/apps/mobile/src/features/tasks/composer/attachments/cloudPrompt.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { CloudPromptBlock } from "./types"; - -/** - * Wire format prefix shared with `packages/shared/src/cloud-prompt.ts`. The - * backend's `deserializeCloudPrompt` looks for this prefix and decodes the - * trailing JSON as `{ blocks: ContentBlock[] }`. Plain-text prompts without - * attachments are sent as strings (no prefix) so chat echoes stay readable. - */ -export const CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:"; - -export function serializeCloudPrompt(blocks: CloudPromptBlock[]): string { - if (blocks.length === 1 && blocks[0].type === "text") { - return blocks[0].text.trim(); - } - return `${CLOUD_PROMPT_PREFIX}${JSON.stringify({ blocks })}`; -} diff --git a/apps/mobile/src/features/tasks/composer/options.test.ts b/apps/mobile/src/features/tasks/composer/options.test.ts index 75328ebf04..c155d51849 100644 --- a/apps/mobile/src/features/tasks/composer/options.test.ts +++ b/apps/mobile/src/features/tasks/composer/options.test.ts @@ -1,27 +1,68 @@ +import { + type CloudTaskConfigOption, + DEFAULT_GATEWAY_MODEL, + restrictedModelMeta, +} from "@posthog/shared"; import { describe, expect, it } from "vitest"; import { - DEFAULT_MODEL, - DEFAULT_REASONING, - modelSupportsReasoning, - REASONING_LEVELS, + getMobileModelOptions, + getModelConfigOption, + getModelLabel, + resolveAvailableModel, } from "./options"; -describe("task composer options", () => { - it("uses an eligible non-premium default model", () => { - expect(DEFAULT_MODEL).toBe("claude-opus-4-8"); - expect(DEFAULT_MODEL).not.toContain("fable"); - }); +const modelOption: CloudTaskConfigOption = { + id: "model", + name: "Model", + type: "select", + currentValue: DEFAULT_GATEWAY_MODEL, + options: [ + { + value: DEFAULT_GATEWAY_MODEL, + name: "Claude Opus 4.8", + description: "Default", + }, + { + value: "claude-fable-5", + name: "Claude Fable 5", + _meta: restrictedModelMeta(), + }, + ], + category: "model", + description: "Choose a model", +}; - it("derives reasoning defaults and options from shared policy", () => { - expect(DEFAULT_REASONING).toBe("high"); - expect(REASONING_LEVELS.map((option) => option.value)).toEqual([ - "low", - "medium", - "high", - "xhigh", - "max", +describe("mobile cloud task model options", () => { + it("adapts live model options and disables restricted entries", () => { + expect(getMobileModelOptions(modelOption)).toEqual([ + { + value: DEFAULT_GATEWAY_MODEL, + label: "Claude Opus 4.8", + description: "Default", + disabled: false, + }, + { + value: "claude-fable-5", + label: "Claude Fable 5", + description: undefined, + disabled: true, + }, ]); - expect(modelSupportsReasoning("claude-opus-4-8")).toBe(true); - expect(modelSupportsReasoning("claude-haiku-4-5")).toBe(false); + }); + + it("falls back from restricted or missing selections", () => { + expect(resolveAvailableModel(modelOption, "claude-fable-5")).toBe( + DEFAULT_GATEWAY_MODEL, + ); + expect(resolveAvailableModel(modelOption, "missing-model")).toBe( + DEFAULT_GATEWAY_MODEL, + ); + }); + + it("reads the live model label and config option", () => { + expect(getModelConfigOption([modelOption])).toBe(modelOption); + expect(getModelLabel(modelOption, DEFAULT_GATEWAY_MODEL)).toBe( + "Claude Opus 4.8", + ); }); }); diff --git a/apps/mobile/src/features/tasks/composer/options.ts b/apps/mobile/src/features/tasks/composer/options.ts index 572fff4a35..1db34b57a1 100644 --- a/apps/mobile/src/features/tasks/composer/options.ts +++ b/apps/mobile/src/features/tasks/composer/options.ts @@ -1,107 +1,56 @@ import { - DEFAULT_CLAUDE_EXECUTION_MODE, - getAvailableModes, -} from "@posthog/core/sessions/executionModes"; -import { - DEFAULT_GATEWAY_MODEL, - DEFAULT_REASONING_EFFORT, - defaultEligibleModel, - getReasoningEffortOptions, - type ExecutionMode as SharedExecutionMode, - type SupportedReasoningEffort, + type CloudTaskConfigOption, + isRestrictedModelOption, } from "@posthog/shared"; -export type ExecutionMode = Extract< - SharedExecutionMode, - "default" | "acceptEdits" | "plan" | "auto" ->; -export type ReasoningEffort = SupportedReasoningEffort; - -export const EXECUTION_MODES: { - value: ExecutionMode; - label: string; - description: string; -}[] = getAvailableModes() - .filter( - (mode): mode is typeof mode & { id: ExecutionMode } => - mode.id === "default" || - mode.id === "acceptEdits" || - mode.id === "plan" || - mode.id === "auto", - ) - .map((mode) => ({ - value: mode.id, - label: mode.name, - description: mode.description, - })); - -export interface ModelOption { +export interface MobileModelOption { value: string; label: string; description?: string; - supportsReasoning: boolean; + disabled: boolean; } -export const MODELS: ModelOption[] = [ - { - value: "claude-fable-5", - label: "Claude Fable 5", - description: "Newest, most capable", - supportsReasoning: true, - }, - { - value: "claude-opus-5", - label: "Claude Opus 5", - description: "Most capable, slower", - supportsReasoning: true, - }, - { - value: "claude-opus-4-8", - label: "Claude Opus 4.8", - description: "Previous Opus generation", - supportsReasoning: true, - }, - { - value: "claude-sonnet-5", - label: "Claude Sonnet 5", - description: "Balanced, fast", - supportsReasoning: true, - }, - { - value: "claude-sonnet-4-6", - label: "Claude Sonnet 4.6", - description: "Balanced", - supportsReasoning: true, - }, -]; - -export const DEFAULT_EXECUTION_MODE: ExecutionMode = - DEFAULT_CLAUDE_EXECUTION_MODE; -export const DEFAULT_MODEL = - defaultEligibleModel(DEFAULT_GATEWAY_MODEL) ?? - MODELS.find((model) => defaultEligibleModel(model.value))?.value ?? - DEFAULT_GATEWAY_MODEL; -export const DEFAULT_REASONING: ReasoningEffort = DEFAULT_REASONING_EFFORT; - -export const REASONING_LEVELS: { - value: ReasoningEffort; - label: string; -}[] = (getReasoningEffortOptions("claude", DEFAULT_MODEL) ?? []).map( - (option) => ({ value: option.value, label: option.name }), -); - -export function modelLabel(value: string): string { - return MODELS.find((m) => m.value === value)?.label ?? value; +export function getModelConfigOption( + configOptions: readonly CloudTaskConfigOption[], +): CloudTaskConfigOption { + const modelOption = configOptions.find( + (option) => option.category === "model", + ); + if (!modelOption) { + throw new Error("Cloud task model configuration is unavailable"); + } + return modelOption; } -export function modeLabel(value: ExecutionMode): string { - return EXECUTION_MODES.find((m) => m.value === value)?.label ?? value; +export function getMobileModelOptions( + modelOption: CloudTaskConfigOption, +): MobileModelOption[] { + return modelOption.options.map((option) => ({ + value: option.value, + label: option.name, + description: option.description, + disabled: isRestrictedModelOption(option._meta), + })); } -export function reasoningLabel(value: ReasoningEffort): string { - return REASONING_LEVELS.find((r) => r.value === value)?.label ?? value; +export function getModelLabel( + modelOption: CloudTaskConfigOption, + value: string, +): string { + return ( + modelOption.options.find((option) => option.value === value)?.name ?? value + ); } -export function modelSupportsReasoning(value: string): boolean { - return getReasoningEffortOptions("claude", value) !== null; +export function resolveAvailableModel( + modelOption: CloudTaskConfigOption, + value: string, +): string { + const selectedOption = modelOption.options.find( + (option) => option.value === value, + ); + if (selectedOption && !isRestrictedModelOption(selectedOption._meta)) { + return value; + } + return modelOption.currentValue; } diff --git a/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts b/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts index 772d9a4f69..fb47817a8c 100644 --- a/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useAutomations.test.ts @@ -8,36 +8,28 @@ const { mockGetTaskAutomations, mockCreateTaskAutomation, mockUpdateTaskAutomation, - mockApiClient, } = vi.hoisted(() => ({ mockUseAuthStore: vi.fn(), mockGetTaskAutomations: vi.fn(), mockCreateTaskAutomation: vi.fn(), mockUpdateTaskAutomation: vi.fn(), - mockApiClient: { - listTaskAutomations: vi.fn(), - getTaskAutomation: vi.fn(), - createTaskAutomation: vi.fn(), - updateTaskAutomation: vi.fn(), - deleteTaskAutomation: vi.fn(), - runTaskAutomation: vi.fn(), - }, })); vi.mock("@/features/auth", () => ({ useAuthStore: mockUseAuthStore, })); -vi.mock("../api", () => ({ runTaskInCloud: vi.fn() })); - vi.mock("@/lib/posthogApiClient", () => ({ - getPostHogApiClient: () => mockApiClient, + getPostHogApiClient: vi.fn(() => ({ + listTaskAutomations: mockGetTaskAutomations, + getTaskAutomation: vi.fn(), + createTaskAutomation: mockCreateTaskAutomation, + updateTaskAutomation: mockUpdateTaskAutomation, + deleteTaskAutomation: vi.fn(), + runTaskAutomation: vi.fn(), + })), })); -mockApiClient.listTaskAutomations = mockGetTaskAutomations; -mockApiClient.createTaskAutomation = mockCreateTaskAutomation; -mockApiClient.updateTaskAutomation = mockUpdateTaskAutomation; - import { automationKeys, getAutomationPollingInterval, diff --git a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts new file mode 100644 index 0000000000..4b516f1445 --- /dev/null +++ b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts @@ -0,0 +1,121 @@ +import { + type CloudTaskConfigOption, + DEFAULT_GATEWAY_MODEL, +} from "@posthog/shared"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { createElement, type PropsWithChildren } from "react"; +import { act, create } from "react-test-renderer"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const { mockGetCloudTaskConfigOptions, mockUseAuthStore } = vi.hoisted(() => ({ + mockGetCloudTaskConfigOptions: vi.fn(), + mockUseAuthStore: vi.fn(), +})); + +vi.mock("posthog-react-native", () => ({ + useFeatureFlag: () => false, +})); + +vi.mock("@/features/auth", () => ({ + useAuthStore: mockUseAuthStore, +})); + +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ + getCloudTaskConfigOptions: mockGetCloudTaskConfigOptions, + }), +})); + +import { getModelConfigOption } from "../composer/options"; +import { useCloudTaskConfigOptions } from "./useCloudTaskConfigOptions"; + +function createWrapper(queryClient: QueryClient) { + return function Wrapper({ children }: PropsWithChildren) { + return createElement( + QueryClientProvider, + { client: queryClient }, + children, + ); + }; +} + +async function renderHook() { + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + let currentResult: ReturnType; + + function HookProbe() { + currentResult = useCloudTaskConfigOptions("claude"); + return null; + } + + const Wrapper = createWrapper(queryClient); + await act(async () => { + create(createElement(Wrapper, null, createElement(HookProbe))); + await Promise.resolve(); + }); + + return { + get current() { + return currentResult; + }, + }; +} + +async function waitForAssertion(assertion: () => void): Promise { + const timeoutAt = Date.now() + 2_000; + while (Date.now() < timeoutAt) { + try { + assertion(); + return; + } catch (error) { + await new Promise((resolve) => setTimeout(resolve, 10)); + if (Date.now() >= timeoutAt) throw error; + } + } +} + +describe("useCloudTaskConfigOptions", () => { + beforeEach(() => { + mockGetCloudTaskConfigOptions.mockReset(); + mockUseAuthStore.mockImplementation((selector) => + selector({ oauthAccessToken: "token" }), + ); + }); + + it("uses the authenticated live Claude catalog", async () => { + const liveOptions: CloudTaskConfigOption[] = [ + { + id: "model", + name: "Model", + type: "select", + currentValue: "claude-sonnet-5", + options: [{ value: "claude-sonnet-5", name: "Claude Sonnet 5" }], + category: "model", + description: "Choose a model", + }, + ]; + mockGetCloudTaskConfigOptions.mockResolvedValue(liveOptions); + + const result = await renderHook(); + await waitForAssertion(() => { + expect(result.current.configOptions).toEqual(liveOptions); + expect(result.current.hasLiveConfig).toBe(true); + }); + expect(mockGetCloudTaskConfigOptions).toHaveBeenCalledWith("claude"); + }); + + it("keeps the shared fallback when unauthenticated", async () => { + mockUseAuthStore.mockImplementation((selector) => + selector({ oauthAccessToken: null }), + ); + + const result = await renderHook(); + + expect( + getModelConfigOption(result.current.configOptions).currentValue, + ).toBe(DEFAULT_GATEWAY_MODEL); + expect(mockGetCloudTaskConfigOptions).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts new file mode 100644 index 0000000000..aaedf3375d --- /dev/null +++ b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts @@ -0,0 +1,52 @@ +import { + type Adapter, + buildCloudTaskConfigOptions, + type CloudTaskConfigOption, + GLM_MODEL_FLAG, + isGlmModelId, +} from "@posthog/shared"; +import { useQuery } from "@tanstack/react-query"; +import { useFeatureFlag } from "posthog-react-native"; +import { useAuthStore } from "@/features/auth"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; + +export const cloudTaskConfigOptionKeys = { + all: ["cloud-task-config-options"] as const, + adapter: (adapter: Adapter) => + [...cloudTaskConfigOptionKeys.all, adapter] as const, +}; + +const fallbackOptionsByAdapter: Record = { + claude: buildCloudTaskConfigOptions([], "claude"), + codex: buildCloudTaskConfigOptions([], "codex"), +}; + +export function useCloudTaskConfigOptions(adapter: Adapter = "claude") { + const oauthAccessToken = useAuthStore((state) => state.oauthAccessToken); + const glmEnabled = useFeatureFlag(GLM_MODEL_FLAG); + const query = useQuery({ + queryKey: cloudTaskConfigOptionKeys.adapter(adapter), + queryFn: () => getPostHogApiClient().getCloudTaskConfigOptions(adapter), + enabled: !!oauthAccessToken, + staleTime: 5 * 60 * 1000, + }); + const configOptions = query.data ?? fallbackOptionsByAdapter[adapter]; + const visibleConfigOptions = glmEnabled + ? configOptions + : configOptions.map((option) => + option.category === "model" + ? { + ...option, + options: option.options.filter( + (model) => !isGlmModelId(model.value), + ), + } + : option, + ); + + return { + ...query, + configOptions: visibleConfigOptions, + hasLiveConfig: query.data !== undefined, + }; +} diff --git a/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts b/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts index 45040a2df6..68394d2aba 100644 --- a/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useIntegrations.test.ts @@ -15,10 +15,10 @@ vi.mock("@/features/auth", () => ({ })); vi.mock("@/lib/posthogApiClient", () => ({ - getPostHogApiClient: () => ({ + getPostHogApiClient: vi.fn(() => ({ getGithubRepositories: mockGetGithubRepositories, getIntegrations: mockGetIntegrations, - }), + })), })); import { useRepositoryCacheStore } from "../stores/repositoryCacheStore"; @@ -123,7 +123,7 @@ describe("useIntegrations", () => { }, ]); mockGetGithubRepositories - .mockResolvedValueOnce(["annika/mobile-app"]) + .mockResolvedValueOnce(["Annika/Mobile-App", ""]) .mockRejectedValueOnce(new Error("GitHub repos failed")); const queryClient = new QueryClient({ diff --git a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts index bf2aab3c77..f0ca06ade5 100644 --- a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts @@ -3,7 +3,7 @@ import { useEffect, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useRepositoryCacheStore } from "../stores/repositoryCacheStore"; -import type { Integration, RepositoryOption } from "../types"; +import type { RepositoryOption } from "../types"; import { buildRepositoryOptions } from "../utils/repositorySelection"; /** Cheap content-equality check for repository option lists. Lets the cache @@ -60,26 +60,7 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { queryKey: integrationKeys.github(), queryFn: async () => { const data = await getPostHogApiClient().getIntegrations(); - return data.flatMap((integration): Integration[] => { - if ( - integration.kind !== "github" || - typeof integration.id !== "number" - ) { - return []; - } - - return [ - { - id: integration.id, - kind: integration.kind, - display_name: - typeof integration.display_name === "string" - ? integration.display_name - : undefined, - config: integration.config as Integration["config"], - }, - ]; - }); + return data.filter((i) => i.kind === "github"); }, enabled: enabled && !!projectId && !!oauthAccessToken, }); @@ -97,9 +78,11 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { const results = await Promise.allSettled( githubIntegrations.map(async (integration) => ({ integrationId: integration.id, - repositories: await getPostHogApiClient().getGithubRepositories( - integration.id, - ), + repositories: ( + await getPostHogApiClient().getGithubRepositories(integration.id) + ) + .map((repository) => repository.toLowerCase()) + .filter((repository) => repository.length > 0), })), ); diff --git a/apps/mobile/src/features/tasks/hooks/useTasks.test.ts b/apps/mobile/src/features/tasks/hooks/useTasks.test.ts index 579ec4bce2..1395c283e8 100644 --- a/apps/mobile/src/features/tasks/hooks/useTasks.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useTasks.test.ts @@ -27,18 +27,15 @@ vi.mock("@/lib/logger", () => { }; }); -vi.mock("../api", () => ({ - runTaskInCloud: vi.fn(), -})); - vi.mock("@/lib/posthogApiClient", () => ({ - getPostHogApiClient: () => ({ + getPostHogApiClient: vi.fn(() => ({ createTask: vi.fn(), deleteTask: vi.fn(), getTask: vi.fn(), getTasks: vi.fn(), + runTaskInCloud: vi.fn(), updateTask: vi.fn(), - }), + })), })); vi.mock("../stores/taskStore", () => ({ diff --git a/apps/mobile/src/features/tasks/hooks/useTasks.ts b/apps/mobile/src/features/tasks/hooks/useTasks.ts index 95b5d4725f..862459708e 100644 --- a/apps/mobile/src/features/tasks/hooks/useTasks.ts +++ b/apps/mobile/src/features/tasks/hooks/useTasks.ts @@ -4,7 +4,6 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useAuthStore, useUserQuery } from "@/features/auth"; import { logger } from "@/lib/logger"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; -import { runTaskInCloud } from "../api"; import { useTaskStore } from "../stores/taskStore"; import type { CreateTaskOptions } from "../types"; @@ -136,13 +135,13 @@ export function useUpdateTask() { }: { taskId: string; updates: Partial; - }) => - getPostHogApiClient().updateTask( + }) => { + const client = getPostHogApiClient(); + return client.updateTask( taskId, - updates as Parameters< - ReturnType["updateTask"] - >[1], - ), + updates as Parameters[1], + ); + }, onSuccess: (updatedTask, { taskId }) => { // Update the detail cache immediately queryClient.setQueryData(taskKeys.detail(taskId), updatedTask); @@ -174,7 +173,8 @@ export function useRunTask() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (taskId: string) => runTaskInCloud(taskId), + mutationFn: (taskId: string) => + getPostHogApiClient().runTaskInCloud(taskId), onSuccess: (updatedTask, taskId) => { queryClient.setQueryData(taskKeys.detail(taskId), updatedTask); queryClient.invalidateQueries({ queryKey: taskKeys.lists() }); diff --git a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts index 1ba6655cf2..0724faad9c 100644 --- a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts @@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query"; import { useCallback, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; -import type { RepositoryOption } from "../types"; +import type { RepositoryOption, UserGithubIntegration } from "../types"; /** * User-scoped sibling of {@link useIntegrations}. Reads the authenticated @@ -29,10 +29,7 @@ interface UseUserIntegrationsOptions { enabled?: boolean; } -function integrationLabel(integration: { - installation_id: string; - account?: { name?: string | null } | null; -}): string { +function integrationLabel(integration: UserGithubIntegration): string { return integration.account?.name ?? `GitHub ${integration.installation_id}`; } @@ -42,7 +39,19 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { const integrationsQuery = useQuery({ queryKey: userIntegrationKeys.github(), - queryFn: () => getPostHogApiClient().getGithubUserIntegrations(), + queryFn: async () => { + const integrations = + await getPostHogApiClient().getGithubUserIntegrations(); + return integrations.map(({ account, ...integration }) => ({ + ...integration, + account: account + ? { + name: account.name ?? undefined, + type: account.type ?? undefined, + } + : undefined, + })); + }, enabled: enabled && !!oauthAccessToken, }); @@ -57,9 +66,13 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { const results = await Promise.allSettled( integrations.map(async (integration) => ({ installationId: integration.installation_id, - repositories: await getPostHogApiClient().getGithubUserRepositories( - integration.installation_id, - ), + repositories: ( + await getPostHogApiClient().getGithubUserRepositories( + integration.installation_id, + ) + ) + .map((repository) => repository.toLowerCase()) + .filter((repository) => repository.length > 0), })), ); diff --git a/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx b/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx index 175a77c276..4985ffeaee 100644 --- a/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx +++ b/apps/mobile/src/features/tasks/hooks/useWarmTask.test.tsx @@ -9,7 +9,9 @@ vi.mock("posthog-react-native", () => ({ useFeatureFlag: () => flagState.enabled, })); vi.mock("@/lib/posthogApiClient", () => ({ - getPostHogApiClient: () => ({ warmTask: mockWarmTask }), + getPostHogApiClient: vi.fn(() => ({ + warmTask: mockWarmTask, + })), })); vi.mock("@/lib/logger", () => { const mockLogger = { diff --git a/apps/mobile/src/features/tasks/index.ts b/apps/mobile/src/features/tasks/index.ts index 7da4db747e..2bcbcc35a7 100644 --- a/apps/mobile/src/features/tasks/index.ts +++ b/apps/mobile/src/features/tasks/index.ts @@ -24,9 +24,3 @@ export { useTaskStore } from "./stores/taskStore"; // Types export * from "./types"; - -// Utils -export { - convertStoredEntriesToEvents, - parseSessionLogs, -} from "./utils/parseSessionLogs"; diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts index 462589a58e..0c57cc6d1a 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts @@ -1,7 +1,9 @@ +import { convertStoredEntriesToPortableSessionEvents } from "@posthog/core/sessions/portableSessionEvents"; import { type CloudTaskUpdatePayload, isTerminalStatus, type StoredLogEntry, + serializeCloudPrompt, type Task, } from "@posthog/shared"; import * as Haptics from "expo-haptics"; @@ -18,7 +20,6 @@ import { sendCloudCommand, } from "../api"; import { buildCloudPromptBlocks } from "../composer/attachments/buildCloudPrompt"; -import { serializeCloudPrompt } from "../composer/attachments/cloudPrompt"; import type { PendingAttachment } from "../composer/attachments/types"; import { type WatchCloudTaskHandle, @@ -31,7 +32,6 @@ import type { SessionNotificationAttachment, TerminalStatus, } from "../types"; -import { convertStoredEntriesToEvents } from "../utils/parseSessionLogs"; import { playbackRateForTaskDuration } from "../utils/playbackRate"; import { reinjectPromptAttachments } from "../utils/promptAttachments"; import { playCompletionSound } from "../utils/sounds"; @@ -991,7 +991,8 @@ export const useTaskSessionStore = create((set, get) => ({ ? update.newEntries : dedupAgainstLocalEchoes(update.newEntries, echoSet); - const events = convertStoredEntriesToEvents(dedupedEntries); + const events = + convertStoredEntriesToPortableSessionEvents(dedupedEntries); // Snapshots are S3-backed and replay user turns as text-only chunks; // reattach the images from the `session/prompt` entries in the same log. if (isSnapshot) { diff --git a/apps/mobile/src/features/tasks/stores/taskStore.ts b/apps/mobile/src/features/tasks/stores/taskStore.ts index 456eae37a1..39276a7eeb 100644 --- a/apps/mobile/src/features/tasks/stores/taskStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskStore.ts @@ -1,11 +1,12 @@ +import type { TaskActivitySortMode } from "@posthog/core/tasks/taskActivity"; +import type { ExecutionMode, SupportedReasoningEffort } from "@posthog/shared"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; -import type { ExecutionMode, ReasoningEffort } from "../composer/options"; import type { RepositorySelection } from "../types"; export type OrganizeMode = "by-project" | "chronological"; -export type SortMode = "created" | "updated"; +export type SortMode = TaskActivitySortMode; const EMPTY_REPOSITORY_SELECTION: RepositorySelection = { integrationId: null, @@ -17,7 +18,7 @@ const EMPTY_REPOSITORY_SELECTION: RepositorySelection = { export interface TaskComposerConfig { mode?: ExecutionMode; model?: string; - reasoning?: ReasoningEffort; + reasoning?: SupportedReasoningEffort; } interface TaskUIState { diff --git a/apps/mobile/src/features/tasks/types.ts b/apps/mobile/src/features/tasks/types.ts index 29a2754d7f..ad45e83576 100644 --- a/apps/mobile/src/features/tasks/types.ts +++ b/apps/mobile/src/features/tasks/types.ts @@ -1,14 +1,8 @@ import type { CloudPermissionOption, CloudTaskPermissionRequestUpdate, - StoredLogEntry as SharedStoredLogEntry, - TaskRunStatus, } from "@posthog/shared"; -export interface MobileStoredLogEntry extends SharedStoredLogEntry { - direction?: "client" | "agent"; -} - export interface SessionNotificationAttachment { kind: "image" | "document"; uri: string; @@ -16,51 +10,12 @@ export interface SessionNotificationAttachment { mimeType?: string; } -export interface SessionNotification { - update?: { - sessionUpdate?: string; - content?: { type: string; text: string }; - // Sidecar carrying user-uploaded attachments on user_message_chunk events. - // The wire format embeds the bytes themselves in a separate serialized - // cloud-prompt payload sent to the agent; this field exists only so the - // local feed can render the attachments alongside the echoed text. - attachments?: SessionNotificationAttachment[]; - title?: string; - toolCallId?: string; - status?: "pending" | "in_progress" | "completed" | "failed" | null; - rawInput?: Record; - rawOutput?: unknown; - entries?: PlanEntry[]; - _meta?: { - claudeCode?: { - toolName?: string; - parentToolCallId?: string; - }; - }; - }; -} - export interface PlanEntry { content: string; status: "pending" | "in_progress" | "completed" | "failed"; priority: string; } -export interface AcpMessage { - type: "acp_message"; - direction: "client" | "agent"; - ts: number; - message: unknown; -} - -export interface SessionUpdateEvent { - type: "session_update"; - ts: number; - notification: SessionNotification; -} - -export type SessionEvent = AcpMessage | SessionUpdateEvent; - export interface CloudPermissionResponseSelection { optionId: string; displayText: string; @@ -75,64 +30,6 @@ export interface CloudPendingPermissionRequest { response?: CloudPermissionResponseSelection; } -export interface TaskRunStateEvent { - type: "task_run_state"; - status?: TaskRunStatus; - stage?: string | null; - output?: Record | null; - error_message?: string | null; - branch?: string | null; - updated_at?: string | null; - completed_at?: string | null; -} - -export interface PermissionRequestEventData { - type: "permission_request"; - requestId: string; - toolCall: CloudTaskPermissionRequestUpdate["toolCall"]; - options: CloudPermissionOption[]; -} - -export interface SseErrorEventData { - error: string; -} - -export function isTaskRunStateEvent(data: unknown): data is TaskRunStateEvent { - return ( - typeof data === "object" && - data !== null && - (data as { type?: string }).type === "task_run_state" - ); -} - -export function isPermissionRequestEvent( - data: unknown, -): data is PermissionRequestEventData { - return ( - typeof data === "object" && - data !== null && - (data as { type?: string }).type === "permission_request" && - typeof (data as { requestId?: string }).requestId === "string" - ); -} - -export function isKeepaliveEvent(data: unknown): boolean { - return ( - typeof data === "object" && - data !== null && - (data as { type?: string }).type === "keepalive" - ); -} - -export function isSseErrorEvent(data: unknown): data is SseErrorEventData { - return ( - typeof data === "object" && - data !== null && - "error" in data && - typeof (data as SseErrorEventData).error === "string" - ); -} - export interface Integration { id: number; kind: string; diff --git a/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts b/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts deleted file mode 100644 index a6d512d59d..0000000000 --- a/apps/mobile/src/features/tasks/utils/parseSessionLogs.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { convertStoredEntriesToPortableSessionEvents } from "@posthog/core/sessions/portableSessionEvents"; -import type { MobileStoredLogEntry, SessionNotification } from "../types"; - -export interface ParsedSessionLogs { - notifications: SessionNotification[]; - rawEntries: MobileStoredLogEntry[]; -} - -export function parseSessionLogs(content: string): ParsedSessionLogs { - if (!content?.trim()) { - return { notifications: [], rawEntries: [] }; - } - - const notifications: SessionNotification[] = []; - const rawEntries: MobileStoredLogEntry[] = []; - - for (const line of content.trim().split("\n")) { - try { - const stored = JSON.parse(line) as MobileStoredLogEntry; - - const msg = stored.notification; - if (msg) { - const hasId = msg.id !== undefined; - const hasMethod = msg.method !== undefined; - const hasResult = msg.result !== undefined || msg.error !== undefined; - - if (hasId && hasMethod) { - stored.direction = "client"; - } else if (hasId && hasResult) { - stored.direction = "agent"; - } else if (hasMethod && !hasId) { - stored.direction = "agent"; - } - } - - rawEntries.push(stored); - - if ( - stored.type === "notification" && - stored.notification?.method === "session/update" && - stored.notification?.params - ) { - notifications.push(stored.notification.params as SessionNotification); - } - } catch { - // Skip malformed lines - } - } - - return { notifications, rawEntries }; -} - -export const convertStoredEntriesToEvents = - convertStoredEntriesToPortableSessionEvents; From d98d556ac1bc960888692f9a4401a2f7a04598c9 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:30:19 +0300 Subject: [PATCH 27/42] refactor(mobile): adopt repository and inbox transport Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/features/inbox/api.ts | 341 +----------------- .../inbox/hooks/useInboxReports.test.ts | 8 +- .../features/inbox/hooks/useInboxReports.ts | 54 +-- .../tasks/composer/RepositoryPickerInline.tsx | 2 +- .../features/tasks/hooks/useIntegrations.ts | 58 ++- .../tasks/hooks/useUserIntegrations.ts | 73 ++-- .../tasks/utils/repositorySelection.test.ts | 99 +++-- .../tasks/utils/repositorySelection.ts | 62 +++- 8 files changed, 219 insertions(+), 478 deletions(-) diff --git a/apps/mobile/src/features/inbox/api.ts b/apps/mobile/src/features/inbox/api.ts index ed6461ce9f..1afbcb3c8b 100644 --- a/apps/mobile/src/features/inbox/api.ts +++ b/apps/mobile/src/features/inbox/api.ts @@ -1,342 +1,11 @@ -import type { DismissalReasonOptionValue } from "@posthog/shared"; -import type { - AnySignalReportArtefact, - AvailableSuggestedReviewer, - AvailableSuggestedReviewersResponse, - CommitDiffResponse, - SignalProcessingStateResponse, - SignalReport, - SignalReportArtefactsResponse, - SignalReportSignalsResponse, - SignalReportsQueryParams, - SignalReportsResponse, - SuggestedReviewerWriteEntry, -} from "@posthog/shared/domain-types"; -import { authedFetch, getBaseUrl, getProjectId, HttpError } from "@/lib/api"; -import { logger } from "@/lib/logger"; - -const log = logger.scope("inbox-api"); - -export async function getSignalReports( - params?: SignalReportsQueryParams, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const url = new URL(`${baseUrl}/api/projects/${projectId}/signals/reports/`); - - if (params?.limit != null) { - url.searchParams.set("limit", String(params.limit)); - } - if (params?.offset != null) { - url.searchParams.set("offset", String(params.offset)); - } - if (params?.status) { - url.searchParams.set("status", params.status); - } - if (params?.ordering) { - url.searchParams.set("ordering", params.ordering); - } - if (params?.source_product) { - url.searchParams.set("source_product", params.source_product); - } - if (params?.suggested_reviewers) { - url.searchParams.set("suggested_reviewers", params.suggested_reviewers); - } - if (params?.priority) { - url.searchParams.set("priority", params.priority); - } - - const response = await authedFetch(url.toString()); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch signal reports", - ); - } - - const data = await response.json(); - return { - results: data.results ?? [], - count: data.count ?? data.results?.length ?? 0, - }; -} - -export async function getSignalReport( - reportId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/`, - ); - - if (response.status === 404 || response.status === 403) { - return null; - } - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch signal report", - ); - } - - return await response.json(); -} - -export async function getSignalProcessingState(): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/processing_state/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch signal processing state", - ); - } - - return await response.json(); -} - -export async function getAvailableSuggestedReviewers( - query?: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const url = new URL( - `${baseUrl}/api/projects/${projectId}/signals/reports/available_reviewers/`, - ); - - if (query?.trim()) { - url.searchParams.set("query", query.trim()); - } - - const response = await authedFetch(url.toString()); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Failed to fetch available suggested reviewers", - ); - } - - // API returns a dict keyed by UUID: { "uuid": { name, email, github_login } } - const data = await response.json(); - const results = Object.entries(data) - .map(([uuid, value]) => { - if (typeof value !== "object" || value === null) return null; - const v = value as Record; - return { - uuid, - name: typeof v.name === "string" ? v.name : "", - email: typeof v.email === "string" ? v.email : "", - github_login: typeof v.github_login === "string" ? v.github_login : "", - }; - }) - .filter((r): r is AvailableSuggestedReviewer => r !== null); - - return { results, count: results.length }; -} - -export async function getSignalReportArtefacts( - reportId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/artefacts/`, - ); - - if (!response.ok) { - const body = await response.text().catch(() => ""); - log.warn("Failed to fetch report artefacts", { - reportId, - status: response.status, - body: body.slice(0, 500), - }); - return { results: [], count: 0 }; - } - - const data = await response.json(); - const results: AnySignalReportArtefact[] = data.results ?? []; - return { results, count: data.count ?? results.length }; -} - -/** Fetch a commit artefact's diff against its parent (lazily, on demand). */ -export async function getCommitDiff( - reportId: string, - artefactId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/artefacts/${artefactId}/diff/`, - ); - - if (!response.ok) { - throw new HttpError( - response.status, - response.statusText, - "Couldn’t load the diff", - ); - } - - const data = await response.json(); - return { - diff: typeof data.diff === "string" ? data.diff : "", - truncated: data.truncated === true, - }; -} - -/** Replace the content of a report artefact (full PUT, not a partial update). */ -export async function updateSignalReportArtefact( - reportId: string, - artefactId: string, - content: SuggestedReviewerWriteEntry[], -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/artefacts/${artefactId}/`, - { - method: "PUT", - body: JSON.stringify({ content }), - }, - ); - - if (!response.ok) { - const errorText = await response.text().catch(() => ""); - throw new HttpError( - response.status, - response.statusText, - errorText || "Failed to update suggested reviewers", - ); - } -} - -export async function getSignalReportSignals( - reportId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/signals/`, - ); - - if (!response.ok) { - log.warn("Failed to fetch report signals", { - reportId, - status: response.status, - }); - return { report: null, signals: [] }; - } - - const data = await response.json(); - return { report: data.report ?? null, signals: data.signals ?? [] }; -} +import { extractRepoSelectionRepository } from "@posthog/core/inbox/artefacts"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; /** Resolve the repository associated with a signal report via its repo_selection artefact. */ export async function getReportRepository( reportId: string, ): Promise { - const { results } = await getSignalReportArtefacts(reportId); - const repoArtefact = results.find((a) => a.type === "repo_selection"); - if (!repoArtefact) return null; - - let parsed: unknown = repoArtefact.content; - if (typeof parsed === "string") { - try { - parsed = JSON.parse(parsed); - } catch { - return (parsed as string).toLowerCase(); - } - } - - if (typeof parsed === "object" && parsed !== null) { - const repo = - (parsed as Record).repository ?? - (parsed as Record).repo; - if (typeof repo === "string") return repo.toLowerCase(); - } - - return null; -} - -export interface DismissSignalReportInput { - reason: DismissalReasonOptionValue; - note?: string; -} - -export async function dismissSignalReport( - reportId: string, - input: DismissSignalReportInput, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/state/`, - { - method: "POST", - body: JSON.stringify({ - state: "suppressed", - dismissal_reason: input.reason, - ...(input.note?.trim() ? { dismissal_note: input.note.trim() } : {}), - }), - }, - ); - - if (!response.ok) { - const errorText = await response.text().catch(() => ""); - throw new HttpError( - response.status, - response.statusText, - errorText || "Failed to dismiss signal report", - ); - } - - return await response.json(); -} - -/** Re-queue a dismissed report into the inbox via the `potential` transition. */ -export async function restoreSignalReport( - reportId: string, -): Promise { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - - const response = await authedFetch( - `${baseUrl}/api/projects/${projectId}/signals/reports/${reportId}/state/`, - { - method: "POST", - body: JSON.stringify({ state: "potential" }), - }, - ); - - if (!response.ok) { - const errorText = await response.text().catch(() => ""); - throw new HttpError( - response.status, - response.statusText, - errorText || "Failed to restore signal report", - ); - } - - return await response.json(); + const { results } = + await getPostHogApiClient().getSignalReportArtefacts(reportId); + return extractRepoSelectionRepository(results); } diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts index ad4abf8190..542886a407 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.test.ts @@ -15,9 +15,11 @@ const getAvailableSuggestedReviewers = vi.fn(async (_query?: string) => ({ results: [], count: 0, })); -vi.mock("../api", () => ({ - getAvailableSuggestedReviewers: (query?: string) => - getAvailableSuggestedReviewers(query), +vi.mock("@/lib/posthogApiClient", () => ({ + getPostHogApiClient: () => ({ + getAvailableSuggestedReviewers: (query?: string) => + getAvailableSuggestedReviewers(query), + }), })); import { diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts index a2d91f0520..e262300bf9 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts @@ -7,6 +7,7 @@ import { INBOX_DISMISSED_STATUS_FILTER, INBOX_REFETCH_INTERVAL_MS, } from "@posthog/core/inbox/reportFiltering"; +import type { DismissalReasonOptionValue } from "@posthog/shared"; import type { AvailableSuggestedReviewersResponse, CommitDiffResponse, @@ -28,19 +29,7 @@ import { } from "@tanstack/react-query"; import { useMemo } from "react"; import { useAuthStore } from "@/features/auth"; -import { - type DismissSignalReportInput, - dismissSignalReport, - getAvailableSuggestedReviewers, - getCommitDiff, - getSignalProcessingState, - getSignalReport, - getSignalReportArtefacts, - getSignalReportSignals, - getSignalReports, - restoreSignalReport, - updateSignalReportArtefact, -} from "../api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useInboxFilterStore } from "../stores/inboxFilterStore"; import { isRestorableReport } from "../utils"; @@ -98,7 +87,7 @@ export function useInboxReports(options?: { enabled?: boolean }) { const query = useInfiniteQuery({ queryKey: inboxKeys.list(params), queryFn: ({ pageParam }) => - getSignalReports({ + getPostHogApiClient().getSignalReports({ ...params, limit: REPORTS_PAGE_SIZE, offset: pageParam, @@ -137,7 +126,7 @@ export function useArchivedReports(options?: { enabled?: boolean }) { const query = useQuery({ queryKey: inboxKeys.archived(params), - queryFn: () => getSignalReports(params), + queryFn: () => getPostHogApiClient().getSignalReports(params), enabled: !!projectId && !!oauthAccessToken && (options?.enabled ?? true), }); @@ -158,7 +147,7 @@ export function useInboxReport(reportId: string | null) { queryKey: inboxKeys.detail(reportId ?? ""), queryFn: () => { if (!reportId) throw new Error("reportId is required"); - return getSignalReport(reportId); + return getPostHogApiClient().getSignalReport(reportId); }, enabled: !!projectId && !!oauthAccessToken && !!reportId, }); @@ -169,7 +158,7 @@ export function useSignalProcessingState(options?: { enabled?: boolean }) { return useQuery({ queryKey: inboxKeys.processingState, - queryFn: () => getSignalProcessingState(), + queryFn: () => getPostHogApiClient().getSignalProcessingState(), enabled: !!projectId && !!oauthAccessToken && (options?.enabled ?? true), refetchInterval: INBOX_REFETCH_INTERVAL_MS, }); @@ -184,7 +173,8 @@ export function useAvailableSuggestedReviewers(options?: { return useQuery({ queryKey: [...inboxKeys.all, "available-reviewers", query] as const, - queryFn: () => getAvailableSuggestedReviewers(query || undefined), + queryFn: () => + getPostHogApiClient().getAvailableSuggestedReviewers(query || undefined), enabled: !!projectId && !!oauthAccessToken && (options?.enabled ?? true), staleTime: 5 * 60 * 1000, // Only poll the unfiltered list; search terms are transient and each one @@ -200,7 +190,7 @@ export function useInboxReportArtefacts(reportId: string | null) { queryKey: inboxKeys.artefacts(reportId ?? ""), queryFn: () => { if (!reportId) throw new Error("reportId is required"); - return getSignalReportArtefacts(reportId); + return getPostHogApiClient().getSignalReportArtefacts(reportId); }, enabled: !!projectId && !!oauthAccessToken && !!reportId, // The log is a live work record — agents append artefacts while a report @@ -219,7 +209,7 @@ export function useCommitDiff( return useQuery({ queryKey: inboxKeys.commitDiff(reportId, artefactId), - queryFn: () => getCommitDiff(reportId, artefactId), + queryFn: () => getPostHogApiClient().getCommitDiff(reportId, artefactId), // A commit's diff is immutable, so only fetch once expanded and never retry. enabled: enabled && !!projectId && !!oauthAccessToken, staleTime: 5 * 60_000, @@ -234,7 +224,7 @@ export function useInboxReportSignals(reportId: string | null) { queryKey: inboxKeys.signals(reportId ?? ""), queryFn: () => { if (!reportId) throw new Error("reportId is required"); - return getSignalReportSignals(reportId); + return getPostHogApiClient().getSignalReportSignals(reportId); }, enabled: !!projectId && !!oauthAccessToken && !!reportId, }); @@ -257,7 +247,9 @@ export function useUpdateSuggestedReviewers(reportId: string) { { previous: SignalReportArtefactsResponse | undefined } >({ mutationFn: ({ artefactId, content }) => - updateSignalReportArtefact(reportId, artefactId, content), + getPostHogApiClient() + .updateSignalReportArtefact(reportId, artefactId, content) + .then(() => undefined), onMutate: async ({ artefactId, optimisticReviewers }) => { await queryClient.cancelQueries({ queryKey }); const previous = @@ -297,8 +289,17 @@ export function useUpdateSuggestedReviewers(reportId: string) { export function useDismissReport(reportId: string) { const queryClient = useQueryClient(); - return useMutation({ - mutationFn: (input) => dismissSignalReport(reportId, input), + return useMutation< + SignalReport, + Error, + { reason: DismissalReasonOptionValue; note?: string } + >({ + mutationFn: (input) => + getPostHogApiClient().updateSignalReportState(reportId, { + state: "suppressed", + dismissal_reason: input.reason, + ...(input.note?.trim() ? { dismissal_note: input.note.trim() } : {}), + }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: inboxKeys.detail(reportId) }); queryClient.invalidateQueries({ queryKey: inboxKeys.all }); @@ -314,11 +315,12 @@ export function useRestoreReport() { // report. return useMutation({ mutationFn: async (reportId) => { - const current = await getSignalReport(reportId); + const client = getPostHogApiClient(); + const current = await client.getSignalReport(reportId); if (current && !isRestorableReport(current)) { return false; } - await restoreSignalReport(reportId); + await client.updateSignalReportState(reportId, { state: "potential" }); return true; }, onSuccess: () => { diff --git a/apps/mobile/src/features/tasks/composer/RepositoryPickerInline.tsx b/apps/mobile/src/features/tasks/composer/RepositoryPickerInline.tsx index e953df43b9..6e39aead06 100644 --- a/apps/mobile/src/features/tasks/composer/RepositoryPickerInline.tsx +++ b/apps/mobile/src/features/tasks/composer/RepositoryPickerInline.tsx @@ -15,8 +15,8 @@ import Animated, { useSharedValue, withTiming, } from "react-native-reanimated"; -import type { RepositoryOption } from "@/features/tasks/types"; import { useThemeColors } from "@/lib/theme"; +import type { RepositoryOption } from "../types"; // Tuning for the nested (ScrollView) path's progressive mount. The first // chunk needs to cover the rows the user can actually see (~5 with the diff --git a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts index f0ca06ade5..25f909f6f9 100644 --- a/apps/mobile/src/features/tasks/hooks/useIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useIntegrations.ts @@ -1,34 +1,14 @@ +import { combineGithubRepositories } from "@posthog/core/integrations/repositories"; import { useQuery } from "@tanstack/react-query"; import { useEffect, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useRepositoryCacheStore } from "../stores/repositoryCacheStore"; -import type { RepositoryOption } from "../types"; -import { buildRepositoryOptions } from "../utils/repositorySelection"; - -/** Cheap content-equality check for repository option lists. Lets the cache - * write effect skip no-op updates, which is what kept retriggering renders - * before — `buildRepositoryOptions` always returns a fresh array, so the - * effect's dep array churned every render. */ -function repositoryOptionsEqual( - a: RepositoryOption[], - b: RepositoryOption[], -): boolean { - if (a === b) return true; - if (a.length !== b.length) return false; - for (let i = 0; i < a.length; i++) { - const left = a[i]; - const right = b[i]; - if ( - left.integrationId !== right.integrationId || - left.repository !== right.repository || - left.integrationLabel !== right.integrationLabel - ) { - return false; - } - } - return true; -} +import { + buildRepositoryOptions, + repositoryLoadWarning, + repositoryOptionsEqual, +} from "../utils/repositorySelection"; export const integrationKeys = { all: ["integrations"] as const, @@ -82,7 +62,7 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { await getPostHogApiClient().getGithubRepositories(integration.id) ) .map((repository) => repository.toLowerCase()) - .filter((repository) => repository.length > 0), + .filter(Boolean), })), ); @@ -100,12 +80,10 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { return { repositoriesByIntegration, - partialError: - failedCount === 0 - ? null - : failedCount === githubIntegrations.length - ? "Could not load GitHub repositories. Pull to retry." - : "Some GitHub repositories could not be loaded. Pull to retry.", + partialError: repositoryLoadWarning( + failedCount, + githubIntegrations.length, + ), }; }, enabled: enabled && githubIntegrations.length > 0, @@ -113,7 +91,19 @@ export function useIntegrations(options: UseIntegrationsOptions = {}) { const repositoriesByIntegration = repositoriesQuery.data?.repositoriesByIntegration ?? {}; - const repositories = Object.values(repositoriesByIntegration).flat().sort(); + const repositories = Object.keys( + combineGithubRepositories( + githubIntegrations.map((integration) => ({ + data: { + integrationId: integration.id, + repos: repositoriesByIntegration[integration.id] ?? [], + }, + isPending: repositoriesQuery.isPending, + isError: false, + isRefetching: repositoriesQuery.isRefetching, + })), + ).repositoryMap, + ).sort(); // Memoize the derived options list keyed on the underlying query data so // its reference is stable across renders when the data hasn't actually diff --git a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts index 0724faad9c..c951950409 100644 --- a/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts +++ b/apps/mobile/src/features/tasks/hooks/useUserIntegrations.ts @@ -1,8 +1,13 @@ +import { combineUserGithubRepositories } from "@posthog/core/integrations/repositories"; import { useQuery } from "@tanstack/react-query"; import { useCallback, useMemo } from "react"; import { useAuthStore } from "@/features/auth"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; -import type { RepositoryOption, UserGithubIntegration } from "../types"; +import type { RepositoryOption } from "../types"; +import { + buildUserRepositoryOptions, + repositoryLoadWarning, +} from "../utils/repositorySelection"; /** * User-scoped sibling of {@link useIntegrations}. Reads the authenticated @@ -29,10 +34,6 @@ interface UseUserIntegrationsOptions { enabled?: boolean; } -function integrationLabel(integration: UserGithubIntegration): string { - return integration.account?.name ?? `GitHub ${integration.installation_id}`; -} - export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { const { enabled = true } = options; const { oauthAccessToken } = useAuthStore(); @@ -62,7 +63,6 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { integrations.map((i) => i.installation_id), ), queryFn: async () => { - const byInstallation: Record = {}; const results = await Promise.allSettled( integrations.map(async (integration) => ({ installationId: integration.installation_id, @@ -72,47 +72,48 @@ export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) { ) ) .map((repository) => repository.toLowerCase()) - .filter((repository) => repository.length > 0), + .filter(Boolean), })), ); - let failedCount = 0; - for (const result of results) { - if (result.status === "fulfilled") { - byInstallation[result.value.installationId] = - result.value.repositories; - } else { - failedCount += 1; - } - } + const combined = combineUserGithubRepositories( + results.map((result) => ({ + data: + result.status === "fulfilled" + ? { + userIntegrationId: + integrations.find( + (integration) => + integration.installation_id === + result.value.installationId, + )?.id ?? "", + installationId: result.value.installationId, + repos: result.value.repositories, + } + : undefined, + isPending: false, + isError: result.status === "rejected", + isRefetching: false, + })), + integrations.map((integration) => integration.installation_id), + ); return { - byInstallation, - partialError: - failedCount === 0 - ? null - : failedCount === integrations.length - ? "Could not load GitHub repositories. Pull to retry." - : "Some GitHub repositories could not be loaded. Pull to retry.", + byInstallation: combined.reposByInstallationId, + partialError: repositoryLoadWarning( + combined.failedInstallationIds.length, + integrations.length, + ), }; }, enabled: enabled && integrations.length > 0, }); const repositoryOptions = useMemo(() => { - const byInstallation = repositoriesQuery.data?.byInstallation ?? {}; - return integrations - .flatMap((integration) => { - const repositories = byInstallation[integration.installation_id] ?? []; - return repositories.map((repository) => ({ - // GitHub installation ids fit in a JS number; use it as the numeric - // key the picker/RepositoryOption already expect. - integrationId: Number(integration.installation_id), - integrationLabel: integrationLabel(integration), - repository, - })); - }) - .sort((left, right) => left.repository.localeCompare(right.repository)); + return buildUserRepositoryOptions( + integrations, + repositoriesQuery.data?.byInstallation ?? {}, + ); }, [integrations, repositoriesQuery.data]); /** Resolve the `UserIntegration` UUID for a selected installation id, to send diff --git a/apps/mobile/src/features/tasks/utils/repositorySelection.test.ts b/apps/mobile/src/features/tasks/utils/repositorySelection.test.ts index 820ef73caf..b7177617e3 100644 --- a/apps/mobile/src/features/tasks/utils/repositorySelection.test.ts +++ b/apps/mobile/src/features/tasks/utils/repositorySelection.test.ts @@ -1,36 +1,31 @@ import { describe, expect, it } from "vitest"; import { buildRepositoryOptions, + buildUserRepositoryOptions, findRepositoryOption, isRepositorySelectionComplete, + repositoryLoadWarning, + repositoryOptionsEqual, toRepositorySelection, } from "./repositorySelection"; describe("repositorySelection", () => { const integrations = [ - { - id: 7, - kind: "github", - display_name: "Personal GitHub", - }, + { id: 7, kind: "github", display_name: "Personal GitHub" }, { id: 11, kind: "github", - config: { - account: { - login: "posthog", - }, - }, + config: { account: { login: "posthog" } }, }, ]; it("preserves integration identity for each repository option", () => { - const options = buildRepositoryOptions(integrations, { - 7: ["annika/mobile-app"], - 11: ["posthog/posthog", "posthog/code"], - }); - - expect(options).toEqual([ + expect( + buildRepositoryOptions(integrations, { + 7: ["annika/mobile-app"], + 11: ["posthog/posthog", "posthog/code"], + }), + ).toEqual([ { integrationId: 7, integrationLabel: "Personal GitHub", @@ -49,41 +44,81 @@ describe("repositorySelection", () => { ]); }); - it("finds the exact repository option when multiple integrations expose the same repository", () => { + it("finds an exact repository option", () => { const options = buildRepositoryOptions(integrations, { 7: ["posthog/posthog"], 11: ["posthog/posthog"], }); - const selected = findRepositoryOption(options, { + expect( + findRepositoryOption(options, { + integrationId: 11, + repository: "posthog/posthog", + }), + ).toEqual({ integrationId: 11, + integrationLabel: "posthog", repository: "posthog/posthog", }); + }); + + it.each([ + [{ installation_id: "42", account: { name: "PostHog" } }, "PostHog"], + [{ installation_id: "43" }, "GitHub 43"], + ])( + "builds user integration options with the expected label", + (integration, integrationLabel) => { + expect( + buildUserRepositoryOptions([integration], { + [integration.installation_id]: ["posthog/code"], + }), + ).toEqual([ + { + integrationId: Number(integration.installation_id), + integrationLabel, + repository: "posthog/code", + }, + ]); + }, + ); - expect(selected).toEqual({ + it("treats a changed label as a different repository option", () => { + const option = { integrationId: 11, integrationLabel: "posthog", - repository: "posthog/posthog", - }); + repository: "posthog/code", + }; + + expect( + repositoryOptionsEqual( + [option], + [{ ...option, integrationLabel: "PostHog GitHub" }], + ), + ).toBe(false); }); - it("converts an option into a reusable repository selection payload", () => { - const options = buildRepositoryOptions(integrations, { - 11: ["posthog/code"], - }); + it.each([ + [0, 2, null], + [1, 2, "Some GitHub repositories could not be loaded. Pull to retry."], + [2, 2, "Could not load GitHub repositories. Pull to retry."], + ])( + "maps repository failures to the expected warning", + (failedCount, totalCount, expected) => { + expect(repositoryLoadWarning(failedCount, totalCount)).toBe(expected); + }, + ); - const selection = toRepositorySelection(options[0] ?? null); + it("converts an option into a repository selection", () => { + const selection = toRepositorySelection({ + integrationId: 11, + integrationLabel: "posthog", + repository: "posthog/code", + }); expect(selection).toEqual({ integrationId: 11, repository: "posthog/code", }); expect(isRepositorySelectionComplete(selection)).toBe(true); - expect( - isRepositorySelectionComplete({ - integrationId: null, - repository: "posthog/code", - }), - ).toBe(false); }); }); diff --git a/apps/mobile/src/features/tasks/utils/repositorySelection.ts b/apps/mobile/src/features/tasks/utils/repositorySelection.ts index 9e7b2cc8a8..bff8441a6a 100644 --- a/apps/mobile/src/features/tasks/utils/repositorySelection.ts +++ b/apps/mobile/src/features/tasks/utils/repositorySelection.ts @@ -2,6 +2,7 @@ import type { Integration, RepositoryOption, RepositorySelection, + UserGithubIntegration, } from "../types"; function getIntegrationLabel(integration: Integration): string { @@ -17,26 +18,67 @@ export function buildRepositoryOptions( repositoriesByIntegration: Record, ): RepositoryOption[] { return integrations - .flatMap((integration) => { - const repositories = repositoriesByIntegration[integration.id] ?? []; - - return repositories.map((repository) => ({ + .flatMap((integration) => + (repositoriesByIntegration[integration.id] ?? []).map((repository) => ({ integrationId: integration.id, integrationLabel: getIntegrationLabel(integration), repository, - })); - }) + })), + ) + .sort((left, right) => left.repository.localeCompare(right.repository)); +} + +export function buildUserRepositoryOptions( + integrations: UserGithubIntegration[], + repositoriesByInstallation: Record, +): RepositoryOption[] { + return integrations + .flatMap((integration) => + (repositoriesByInstallation[integration.installation_id] ?? []).map( + (repository) => ({ + integrationId: Number(integration.installation_id), + integrationLabel: + integration.account?.name ?? + `GitHub ${integration.installation_id}`, + repository, + }), + ), + ) .sort((left, right) => left.repository.localeCompare(right.repository)); } +export function repositoryOptionsEqual( + left: RepositoryOption[], + right: RepositoryOption[], +): boolean { + return ( + left.length === right.length && + left.every((option, index) => { + const other = right[index]; + return ( + other?.integrationId === option.integrationId && + other.integrationLabel === option.integrationLabel && + other.repository === option.repository + ); + }) + ); +} + +export function repositoryLoadWarning( + failedCount: number, + totalCount: number, +): string | null { + if (failedCount === 0) return null; + return failedCount === totalCount + ? "Could not load GitHub repositories. Pull to retry." + : "Some GitHub repositories could not be loaded. Pull to retry."; +} + export function findRepositoryOption( options: RepositoryOption[], selection: RepositorySelection, ): RepositoryOption | null { - if (!selection.integrationId || !selection.repository) { - return null; - } - + if (!selection.integrationId || !selection.repository) return null; return ( options.find( (option) => From 2a5f2257506e3ad106c3316c0a8e9f8992242b66 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:36:16 +0300 Subject: [PATCH 28/42] refactor(mobile): adopt shared MCP and composer policies Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/index.tsx | 51 +++-- apps/mobile/src/features/mcp/api.ts | 185 ------------------ apps/mobile/src/features/mcp/hooks.ts | 49 ++--- apps/mobile/src/features/mcp/oauth.ts | 40 ++-- .../tasks/components/PlanApprovalCard.tsx | 71 ++----- .../tasks/composer/TaskChatComposer.tsx | 57 ++++-- .../features/tasks/composer/options.test.ts | 70 ++++--- .../src/features/tasks/composer/options.ts | 70 ++++--- .../src/features/tasks/skills/api.test.ts | 92 --------- apps/mobile/src/features/tasks/skills/api.ts | 42 ---- .../mobile/src/features/tasks/skills/hooks.ts | 6 +- .../stores/pendingPromptRecoveryStore.ts | 27 +-- .../tasks/stores/pendingTaskPromptStore.ts | 10 +- 13 files changed, 235 insertions(+), 535 deletions(-) delete mode 100644 apps/mobile/src/features/mcp/api.ts delete mode 100644 apps/mobile/src/features/tasks/skills/api.test.ts delete mode 100644 apps/mobile/src/features/tasks/skills/api.ts diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index 9df757f9de..6f567488a6 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -3,6 +3,7 @@ import { DEFAULT_CLAUDE_EXECUTION_MODE, getAvailableModes, } from "@posthog/core/sessions/executionModes"; +import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, @@ -56,10 +57,10 @@ import { import type { PendingAttachment } from "@/features/tasks/composer/attachments/types"; import { DotBackground } from "@/features/tasks/composer/DotBackground"; import { - getMobileModelOptions, + getComposerModelOptions, + getConfigOptionLabel, + getMobileExecutionModes, getModelConfigOption, - getModelLabel, - resolveAvailableModel, } from "@/features/tasks/composer/options"; import { Pill } from "@/features/tasks/composer/Pill"; import { RepositoryPickerInline } from "@/features/tasks/composer/RepositoryPickerInline"; @@ -88,7 +89,7 @@ import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { toRgba, useThemeColors } from "@/lib/theme"; const log = logger.scope("task-create"); -const EXECUTION_MODES = getAvailableModes(); +const EXECUTION_MODES = getMobileExecutionModes(getAvailableModes()); const SUGGESTIONS = [ "Create or update my CLAUDE.md file", @@ -129,9 +130,10 @@ export default function NewTaskScreen() { const { insets, bottom } = useScreenInsets(); const keyboard = useReanimatedKeyboardAnimation(); const restingBottom = bottom("compact"); - const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); + const { configOptions, hasLiveConfig, isConfigReady } = + useCloudTaskConfigOptions("claude"); const modelConfigOption = getModelConfigOption(configOptions); - const mobileModelOptions = getMobileModelOptions(modelConfigOption); + const mobileModelOptions = getComposerModelOptions(modelConfigOption); const { error, hasGithubIntegration, @@ -214,12 +216,14 @@ export default function NewTaskScreen() { useEffect(() => { if (!hasLiveConfig) return; - const availableModel = resolveAvailableModel(modelConfigOption, model); - if (availableModel === model) return; - setModel(availableModel); - if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) { - setReasoning(DEFAULT_REASONING_EFFORT); - } + const next = resolveCloudComposerModelChange({ + adapter: "claude", + modelOption: modelConfigOption, + requestedModel: model, + reasoning, + }); + if (next.model !== model) setModel(next.model); + if (next.reasoning !== reasoning) setReasoning(next.reasoning); }, [hasLiveConfig, model, modelConfigOption, reasoning]); const [creating, setCreating] = useState(false); const [repoSheetOpen, setRepoSheetOpen] = useState(false); @@ -411,7 +415,7 @@ export default function NewTaskScreen() { const hasContent = !!prompt.trim() || attachments.length > 0; const canSubmit = - hasLiveConfig && + isConfigReady && hasContent && isRepositorySelectionComplete(selection) && !creating; @@ -424,7 +428,7 @@ export default function NewTaskScreen() { useWarmTask({ repository: selection.repository, githubIntegrationId: selection.integrationId, - composerIsEmpty: !hasContent || !hasLiveConfig, + composerIsEmpty: !hasContent || !isConfigReady, runtimeAdapter: "claude", model, reasoningEffort: showReasoningPill ? reasoning : null, @@ -649,7 +653,12 @@ export default function NewTaskScreen() { } - label={getModelLabel(modelConfigOption, model)} + label={ + getConfigOptionLabel( + modelConfigOption.options, + model, + ) ?? model + } onPress={() => setModelSheetOpen(true)} /> @@ -769,10 +778,14 @@ export default function NewTaskScreen() { title="Model" value={model} onChange={(value) => { - setModel(value); - if (!isSupportedReasoningEffort("claude", value, reasoning)) { - setReasoning(DEFAULT_REASONING_EFFORT); - } + const next = resolveCloudComposerModelChange({ + adapter: "claude", + modelOption: modelConfigOption, + requestedModel: value, + reasoning, + }); + setModel(next.model); + setReasoning(next.reasoning); }} onClose={() => setModelSheetOpen(false)} options={mobileModelOptions.map((modelOption) => ({ diff --git a/apps/mobile/src/features/mcp/api.ts b/apps/mobile/src/features/mcp/api.ts deleted file mode 100644 index f7fa0a828c..0000000000 --- a/apps/mobile/src/features/mcp/api.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { authedFetch, getBaseUrl, getProjectId } from "@/lib/api"; -import type { - InstallCustomMcpServerOptions, - InstallMcpTemplateOptions, - McpApprovalState, - McpInstallationTool, - McpInstallResponse, - McpOAuthRedirectResponse, - McpRecommendedServer, - McpServerInstallation, - UpdateMcpServerInstallationOptions, -} from "./types"; - -function mcpBaseUrl(): string { - const base = getBaseUrl(); - const projectId = getProjectId(); - return `${base}/api/environments/${projectId}/mcp_server_installations`; -} - -async function readJsonOrThrow( - response: Response, - errorPrefix: string, -): Promise { - if (!response.ok) { - const data = (await response.json().catch(() => ({}))) as { - detail?: string; - }; - throw new Error(data.detail ?? `${errorPrefix}: ${response.statusText}`); - } - return (await response.json()) as T; -} - -/** GET /api/environments/{teamId}/mcp_servers/ — marketplace templates. */ -export async function getMcpRecommendedServers(): Promise< - McpRecommendedServer[] -> { - const base = getBaseUrl(); - const projectId = getProjectId(); - const response = await authedFetch( - `${base}/api/environments/${projectId}/mcp_servers/`, - ); - const data = await readJsonOrThrow< - McpRecommendedServer[] | { results?: McpRecommendedServer[] } - >(response, "Failed to fetch MCP servers"); - return Array.isArray(data) ? data : (data.results ?? []); -} - -/** GET /api/environments/{teamId}/mcp_server_installations/ */ -export async function getMcpServerInstallations(): Promise< - McpServerInstallation[] -> { - const response = await authedFetch(`${mcpBaseUrl()}/`); - const data = await readJsonOrThrow< - McpServerInstallation[] | { results?: McpServerInstallation[] } - >(response, "Failed to fetch MCP server installations"); - return Array.isArray(data) ? data : (data.results ?? []); -} - -/** POST /api/environments/{teamId}/mcp_server_installations/install_custom/ */ -export async function installCustomMcpServer( - options: InstallCustomMcpServerOptions, -): Promise { - const response = await authedFetch(`${mcpBaseUrl()}/install_custom/`, { - method: "POST", - body: JSON.stringify(options), - }); - return readJsonOrThrow( - response, - "Failed to install MCP server", - ); -} - -/** POST /api/environments/{teamId}/mcp_server_installations/install_template/ */ -export async function installMcpTemplate( - options: InstallMcpTemplateOptions, -): Promise { - const response = await authedFetch(`${mcpBaseUrl()}/install_template/`, { - method: "POST", - body: JSON.stringify(options), - }); - return readJsonOrThrow( - response, - "Failed to install MCP template", - ); -} - -/** PATCH /api/environments/{teamId}/mcp_server_installations/{id}/ */ -export async function updateMcpServerInstallation( - installationId: string, - updates: UpdateMcpServerInstallationOptions, -): Promise { - const response = await authedFetch(`${mcpBaseUrl()}/${installationId}/`, { - method: "PATCH", - body: JSON.stringify(updates), - }); - return readJsonOrThrow( - response, - "Failed to update MCP server", - ); -} - -/** DELETE /api/environments/{teamId}/mcp_server_installations/{id}/ */ -export async function uninstallMcpServer( - installationId: string, -): Promise { - const response = await authedFetch(`${mcpBaseUrl()}/${installationId}/`, { - method: "DELETE", - }); - if (!response.ok && response.status !== 204) { - throw new Error(`Failed to uninstall MCP server: ${response.statusText}`); - } -} - -/** GET /api/environments/{teamId}/mcp_server_installations/authorize/?installation_id={id} */ -export async function authorizeMcpInstallation(options: { - installation_id: string; - install_source?: "posthog" | "posthog-code" | "posthog-mobile"; - posthog_code_callback_url?: string; -}): Promise { - const params = new URLSearchParams(); - params.set("installation_id", options.installation_id); - if (options.install_source) { - params.set("install_source", options.install_source); - } - if (options.posthog_code_callback_url) { - params.set("posthog_code_callback_url", options.posthog_code_callback_url); - } - const response = await authedFetch( - `${mcpBaseUrl()}/authorize/?${params.toString()}`, - ); - return readJsonOrThrow( - response, - "Failed to authorize MCP installation", - ); -} - -/** GET /api/environments/{teamId}/mcp_server_installations/{id}/tools/ */ -export async function getMcpInstallationTools( - installationId: string, - options: { includeRemoved?: boolean } = {}, -): Promise { - const params = new URLSearchParams(); - if (options.includeRemoved) params.set("include_removed", "1"); - const query = params.toString(); - const response = await authedFetch( - `${mcpBaseUrl()}/${installationId}/tools/${query ? `?${query}` : ""}`, - ); - const data = await readJsonOrThrow< - McpInstallationTool[] | { results?: McpInstallationTool[] } - >(response, "Failed to fetch MCP installation tools"); - return Array.isArray(data) ? data : (data.results ?? []); -} - -/** PATCH /api/environments/{teamId}/mcp_server_installations/{id}/tools/{name}/ */ -export async function updateMcpToolApproval( - installationId: string, - toolName: string, - approval_state: McpApprovalState, -): Promise { - const response = await authedFetch( - `${mcpBaseUrl()}/${installationId}/tools/${encodeURIComponent(toolName)}/`, - { - method: "PATCH", - body: JSON.stringify({ approval_state }), - }, - ); - return readJsonOrThrow( - response, - "Failed to update tool approval", - ); -} - -/** POST /api/environments/{teamId}/mcp_server_installations/{id}/tools/refresh/ */ -export async function refreshMcpInstallationTools( - installationId: string, -): Promise { - const response = await authedFetch( - `${mcpBaseUrl()}/${installationId}/tools/refresh/`, - { method: "POST" }, - ); - const data = await readJsonOrThrow< - McpInstallationTool[] | { results?: McpInstallationTool[] } - >(response, "Failed to refresh MCP tools"); - return Array.isArray(data) ? data : (data.results ?? []); -} diff --git a/apps/mobile/src/features/mcp/hooks.ts b/apps/mobile/src/features/mcp/hooks.ts index 9c724aea1e..4ecf708d61 100644 --- a/apps/mobile/src/features/mcp/hooks.ts +++ b/apps/mobile/src/features/mcp/hooks.ts @@ -1,16 +1,5 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { - authorizeMcpInstallation, - getMcpInstallationTools, - getMcpRecommendedServers, - getMcpServerInstallations, - installCustomMcpServer, - installMcpTemplate, - refreshMcpInstallationTools, - uninstallMcpServer, - updateMcpServerInstallation, - updateMcpToolApproval, -} from "./api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import type { InstallCustomMcpServerOptions, InstallMcpTemplateOptions, @@ -29,7 +18,7 @@ const mcpKeys = { export function useMcpMarketplace() { return useQuery({ queryKey: mcpKeys.marketplace(), - queryFn: getMcpRecommendedServers, + queryFn: () => getPostHogApiClient().getMcpServers(), staleTime: 5 * 60 * 1000, }); } @@ -37,7 +26,7 @@ export function useMcpMarketplace() { export function useMcpInstallations() { return useQuery({ queryKey: mcpKeys.installations(), - queryFn: getMcpServerInstallations, + queryFn: () => getPostHogApiClient().getMcpServerInstallations(), staleTime: 30 * 1000, }); } @@ -45,7 +34,8 @@ export function useMcpInstallations() { export function useMcpInstallationTools(installationId: string | null) { return useQuery({ queryKey: mcpKeys.tools(installationId ?? ""), - queryFn: () => getMcpInstallationTools(installationId as string), + queryFn: () => + getPostHogApiClient().getMcpInstallationTools(installationId as string), enabled: !!installationId, staleTime: 30 * 1000, }); @@ -61,7 +51,7 @@ export function useInstallCustomMcpServer() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (options: InstallCustomMcpServerOptions) => - installCustomMcpServer(options), + getPostHogApiClient().installCustomMcpServer(options), onSuccess: () => invalidateInstallations(queryClient), }); } @@ -70,7 +60,7 @@ export function useInstallMcpTemplate() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (options: InstallMcpTemplateOptions) => - installMcpTemplate(options), + getPostHogApiClient().installMcpTemplate(options), onSuccess: () => invalidateInstallations(queryClient), }); } @@ -84,7 +74,11 @@ export function useUpdateMcpServerInstallation() { }: { installationId: string; updates: UpdateMcpServerInstallationOptions; - }) => updateMcpServerInstallation(installationId, updates), + }) => + getPostHogApiClient().updateMcpServerInstallation( + installationId, + updates, + ), onSuccess: () => invalidateInstallations(queryClient), }); } @@ -92,15 +86,19 @@ export function useUpdateMcpServerInstallation() { export function useUninstallMcpServer() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (installationId: string) => uninstallMcpServer(installationId), + mutationFn: (installationId: string) => + getPostHogApiClient().uninstallMcpServer(installationId), onSuccess: () => invalidateInstallations(queryClient), }); } export function useAuthorizeMcpInstallation() { return useMutation({ - mutationFn: (args: Parameters[0]) => - authorizeMcpInstallation(args), + mutationFn: (args: { + installation_id: string; + install_source?: "posthog" | "posthog-code" | "posthog-mobile"; + posthog_code_callback_url?: string; + }) => getPostHogApiClient().authorizeMcpInstallation(args), }); } @@ -108,7 +106,7 @@ export function useRefreshMcpInstallationTools() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (installationId: string) => - refreshMcpInstallationTools(installationId), + getPostHogApiClient().refreshMcpInstallationTools(installationId), onSuccess: (_, installationId) => { queryClient.invalidateQueries({ queryKey: mcpKeys.tools(installationId), @@ -129,7 +127,12 @@ export function useUpdateMcpToolApproval() { installationId: string; toolName: string; approval_state: McpApprovalState; - }) => updateMcpToolApproval(installationId, toolName, approval_state), + }) => + getPostHogApiClient().updateMcpToolApproval( + installationId, + toolName, + approval_state, + ), onSuccess: (_, { installationId }) => { queryClient.invalidateQueries({ queryKey: mcpKeys.tools(installationId), diff --git a/apps/mobile/src/features/mcp/oauth.ts b/apps/mobile/src/features/mcp/oauth.ts index 190fabd6bf..0c91a6cdf1 100644 --- a/apps/mobile/src/features/mcp/oauth.ts +++ b/apps/mobile/src/features/mcp/oauth.ts @@ -1,10 +1,6 @@ import * as Linking from "expo-linking"; import * as WebBrowser from "expo-web-browser"; -import { - authorizeMcpInstallation, - installCustomMcpServer, - installMcpTemplate, -} from "./api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; import type { InstallCustomMcpServerOptions, InstallMcpTemplateOptions, @@ -51,11 +47,12 @@ export async function installTemplateWithOAuth( "install_source" | "posthog_code_callback_url" >, ): Promise { - const response: McpInstallResponse = await installMcpTemplate({ - ...options, - install_source: INSTALL_SOURCE, - posthog_code_callback_url: OAUTH_CALLBACK_URL, - }); + const response: McpInstallResponse = + await getPostHogApiClient().installMcpTemplate({ + ...options, + install_source: INSTALL_SOURCE, + posthog_code_callback_url: OAUTH_CALLBACK_URL, + }); if (!isOAuthRedirect(response)) return response; @@ -75,11 +72,12 @@ export async function installCustomWithOAuth( "install_source" | "posthog_code_callback_url" >, ): Promise { - const response: McpInstallResponse = await installCustomMcpServer({ - ...options, - install_source: INSTALL_SOURCE, - posthog_code_callback_url: OAUTH_CALLBACK_URL, - }); + const response: McpInstallResponse = + await getPostHogApiClient().installCustomMcpServer({ + ...options, + install_source: INSTALL_SOURCE, + posthog_code_callback_url: OAUTH_CALLBACK_URL, + }); if (!isOAuthRedirect(response)) return response; const outcome = await waitForOAuthCallback(response.redirect_url); @@ -94,11 +92,13 @@ export async function installCustomWithOAuth( export async function reauthorizeInstallation( installationId: string, ): Promise<"completed" | "cancelled"> { - const { redirect_url } = await authorizeMcpInstallation({ - installation_id: installationId, - install_source: INSTALL_SOURCE, - posthog_code_callback_url: OAUTH_CALLBACK_URL, - }); + const { redirect_url } = await getPostHogApiClient().authorizeMcpInstallation( + { + installation_id: installationId, + install_source: INSTALL_SOURCE, + posthog_code_callback_url: OAUTH_CALLBACK_URL, + }, + ); return waitForOAuthCallback(redirect_url); } diff --git a/apps/mobile/src/features/tasks/components/PlanApprovalCard.tsx b/apps/mobile/src/features/tasks/components/PlanApprovalCard.tsx index 6781c7cdff..a03ff18bea 100644 --- a/apps/mobile/src/features/tasks/components/PlanApprovalCard.tsx +++ b/apps/mobile/src/features/tasks/components/PlanApprovalCard.tsx @@ -1,3 +1,9 @@ +import { + getPermissionOptionMeta, + isPermissionRejection, + permissionOptionUsesCustomInput, +} from "@posthog/core/sessions/permissionResponse"; +import { extractPlanText } from "@posthog/core/sessions/planApprovalPresentation"; import { ArrowsClockwise, ChatCircle, @@ -28,56 +34,6 @@ interface PlanApprovalCardProps { onSendPermissionResponse?: (args: PermissionResponseArgs) => void; } -function optionMeta(option: CloudPendingPermissionRequest["options"][number]) { - return option._meta as - | { - customInput?: boolean; - description?: string; - } - | undefined; -} - -function isRejectOption( - option?: CloudPendingPermissionRequest["options"][number], -) { - if (!option) return false; - return option.kind.startsWith("reject") || option.optionId.includes("reject"); -} - -function extractTextContent(item: unknown): string | null { - if (!item || typeof item !== "object") return null; - - const record = item as Record; - if (typeof record.text === "string") { - return record.text; - } - - if (!record.content || typeof record.content !== "object") { - return null; - } - - const content = record.content as Record; - return typeof content.text === "string" ? content.text : null; -} - -function extractPlanText( - permission?: CloudPendingPermissionRequest, -): string | null { - const rawPlan = permission?.toolCall.rawInput?.plan; - if (typeof rawPlan === "string" && rawPlan.trim().length > 0) { - return rawPlan; - } - - for (const item of permission?.toolCall.content ?? []) { - const text = extractTextContent(item); - if (text?.trim()) { - return text; - } - } - - return null; -} - export function PlanApprovalCard({ toolData, permission, @@ -90,7 +46,10 @@ export function PlanApprovalCard({ const [customInput, setCustomInput] = useState(""); const response = permission?.response; - const planText = useMemo(() => extractPlanText(permission), [permission]); + const planText = useMemo( + () => (permission ? extractPlanText(permission.toolCall) : null), + [permission], + ); const selectedOption = useMemo( () => permission?.options.find( @@ -132,7 +91,9 @@ export function PlanApprovalCard({ selectedOption?.name || response?.displayText || null; - const resolvedAsReject = isRejectOption(selectedOption); + const resolvedAsReject = selectedOption + ? isPermissionRejection(selectedOption) + : false; return ( @@ -196,8 +157,8 @@ export function PlanApprovalCard({ ) : ( {permission.options.map((option) => { - const meta = optionMeta(option); - const usesCustomInput = meta?.customInput === true; + const meta = getPermissionOptionMeta(option); + const usesCustomInput = permissionOptionUsesCustomInput(option); const isCustomSelected = selectedCustomOptionId === option.optionId; return ( @@ -225,7 +186,7 @@ export function PlanApprovalCard({ > {option.name} - {meta?.description && ( + {meta.description && ( {meta.description} diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index b61e6f732e..e395b7cf00 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -3,12 +3,12 @@ import { DEFAULT_CLAUDE_EXECUTION_MODE, getAvailableModes, } from "@posthog/core/sessions/executionModes"; +import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, getReasoningEffortOptions, - isSupportedReasoningEffort, type SupportedReasoningEffort, } from "@posthog/shared"; import * as Haptics from "expo-haptics"; @@ -57,10 +57,11 @@ import { } from "./attachments/pickers"; import type { PendingAttachment } from "./attachments/types"; import { - getMobileModelOptions, + getComposerModelOptions, + getConfigOptionLabel, + getMobileExecutionModes, getModelConfigOption, - getModelLabel, - resolveAvailableModel, + resolveComposerPrimaryAction, } from "./options"; import { Pill } from "./Pill"; import { SelectSheet } from "./SelectSheet"; @@ -71,7 +72,7 @@ import { } from "./submitComposerMessage"; const log = logger.scope("task-chat-composer"); -const EXECUTION_MODES = getAvailableModes(); +const EXECUTION_MODES = getMobileExecutionModes(getAvailableModes()); interface TaskChatComposerProps { onSend: ( @@ -196,7 +197,7 @@ export function TaskChatComposer({ const themeColors = useThemeColors(); const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); const modelConfigOption = getModelConfigOption(configOptions); - const mobileModelOptions = getMobileModelOptions(modelConfigOption); + const mobileModelOptions = getComposerModelOptions(modelConfigOption); const [message, setMessage] = useState(() => initialMessage ?? ""); const [attachments, setAttachments] = useState([]); const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false); @@ -222,12 +223,14 @@ export function TaskChatComposer({ useEffect(() => { if (!hasLiveConfig) return; - const availableModel = resolveAvailableModel(modelConfigOption, model); - if (availableModel === model) return; - onModelChange(availableModel); - if (!isSupportedReasoningEffort("claude", availableModel, reasoning)) { - onReasoningChange(DEFAULT_REASONING_EFFORT); - } + const next = resolveCloudComposerModelChange({ + adapter: "claude", + modelOption: modelConfigOption, + requestedModel: model, + reasoning, + }); + if (next.model !== model) onModelChange(next.model); + if (next.reasoning !== reasoning) onReasoningChange(next.reasoning); }, [ hasLiveConfig, model, @@ -255,9 +258,16 @@ export function TaskChatComposer({ const showReasoningPill = reasoningOptions.length > 0; const hasContent = !isComposerEmpty({ text: message, attachments }); - const canSend = hasContent && !disabled && !isRecording; - const showStop = - !isUserTurn && !canSend && !isRecording && !isTranscribing && !!onStop; + const primaryAction = resolveComposerPrimaryAction({ + hasContent, + disabled, + isRecording, + isTranscribing, + canStop: !isUserTurn && !!onStop, + allowSendWhileRunning: true, + }); + const canSend = primaryAction === "send"; + const showStop = primaryAction === "stop"; const applyContent = (content: ComposerContent) => { setMessage(content.text); @@ -441,7 +451,10 @@ export function TaskChatComposer({ } - label={getModelLabel(modelConfigOption, model)} + label={ + getConfigOptionLabel(modelConfigOption.options, model) ?? + model + } onPress={() => setModelSheetOpen(true)} /> @@ -517,10 +530,14 @@ export function TaskChatComposer({ title="Model" value={model} onChange={(v) => { - onModelChange(v); - if (!isSupportedReasoningEffort("claude", v, reasoning)) { - onReasoningChange(DEFAULT_REASONING_EFFORT); - } + const next = resolveCloudComposerModelChange({ + adapter: "claude", + modelOption: modelConfigOption, + requestedModel: v, + reasoning, + }); + onModelChange(next.model); + onReasoningChange(next.reasoning); }} onClose={() => setModelSheetOpen(false)} options={mobileModelOptions.map((m) => ({ diff --git a/apps/mobile/src/features/tasks/composer/options.test.ts b/apps/mobile/src/features/tasks/composer/options.test.ts index c155d51849..76675adc5d 100644 --- a/apps/mobile/src/features/tasks/composer/options.test.ts +++ b/apps/mobile/src/features/tasks/composer/options.test.ts @@ -5,10 +5,9 @@ import { } from "@posthog/shared"; import { describe, expect, it } from "vitest"; import { - getMobileModelOptions, - getModelConfigOption, - getModelLabel, - resolveAvailableModel, + getComposerModelOptions, + getMobileExecutionModes, + resolveComposerPrimaryAction, } from "./options"; const modelOption: CloudTaskConfigOption = { @@ -17,11 +16,7 @@ const modelOption: CloudTaskConfigOption = { type: "select", currentValue: DEFAULT_GATEWAY_MODEL, options: [ - { - value: DEFAULT_GATEWAY_MODEL, - name: "Claude Opus 4.8", - description: "Default", - }, + { value: DEFAULT_GATEWAY_MODEL, name: "Claude Opus 4.8" }, { value: "claude-fable-5", name: "Claude Fable 5", @@ -32,13 +27,31 @@ const modelOption: CloudTaskConfigOption = { description: "Choose a model", }; -describe("mobile cloud task model options", () => { - it("adapts live model options and disables restricted entries", () => { - expect(getMobileModelOptions(modelOption)).toEqual([ +describe("mobile composer options", () => { + it("hides unrestricted execution modes", () => { + expect( + getMobileExecutionModes([ + { id: "plan", name: "Plan", description: "Plan first" }, + { + id: "bypassPermissions", + name: "Bypass permissions", + description: "Allow everything", + }, + { + id: "full-access", + name: "Full access", + description: "Allow everything", + }, + ]).map((mode) => mode.id), + ).toEqual(["plan"]); + }); + + it("adapts live model options for the mobile picker", () => { + expect(getComposerModelOptions(modelOption)).toEqual([ { value: DEFAULT_GATEWAY_MODEL, label: "Claude Opus 4.8", - description: "Default", + description: undefined, disabled: false, }, { @@ -50,19 +63,22 @@ describe("mobile cloud task model options", () => { ]); }); - it("falls back from restricted or missing selections", () => { - expect(resolveAvailableModel(modelOption, "claude-fable-5")).toBe( - DEFAULT_GATEWAY_MODEL, - ); - expect(resolveAvailableModel(modelOption, "missing-model")).toBe( - DEFAULT_GATEWAY_MODEL, - ); - }); - - it("reads the live model label and config option", () => { - expect(getModelConfigOption([modelOption])).toBe(modelOption); - expect(getModelLabel(modelOption, DEFAULT_GATEWAY_MODEL)).toBe( - "Claude Opus 4.8", - ); + it.each([ + [{ hasContent: true }, "send"], + [{ canStop: true }, "stop"], + [{ isRecording: true }, "mic-stop"], + [{}, "mic"], + ])("derives the mobile primary action", (overrides, expected) => { + expect( + resolveComposerPrimaryAction({ + hasContent: false, + disabled: false, + isRecording: false, + isTranscribing: false, + canStop: false, + allowSendWhileRunning: true, + ...overrides, + }), + ).toBe(expected); }); }); diff --git a/apps/mobile/src/features/tasks/composer/options.ts b/apps/mobile/src/features/tasks/composer/options.ts index 1db34b57a1..35daf8f3e0 100644 --- a/apps/mobile/src/features/tasks/composer/options.ts +++ b/apps/mobile/src/features/tasks/composer/options.ts @@ -1,3 +1,4 @@ +import type { ModeInfo } from "@posthog/core/sessions/executionModes"; import { type CloudTaskConfigOption, isRestrictedModelOption, @@ -10,19 +11,23 @@ export interface MobileModelOption { disabled: boolean; } +export function getMobileExecutionModes( + modes: readonly ModeInfo[], +): ModeInfo[] { + return modes.filter( + (mode) => mode.id !== "bypassPermissions" && mode.id !== "full-access", + ); +} + export function getModelConfigOption( configOptions: readonly CloudTaskConfigOption[], ): CloudTaskConfigOption { - const modelOption = configOptions.find( - (option) => option.category === "model", - ); - if (!modelOption) { - throw new Error("Cloud task model configuration is unavailable"); - } - return modelOption; + const option = configOptions.find((item) => item.category === "model"); + if (!option) throw new Error("Cloud task model configuration is unavailable"); + return option; } -export function getMobileModelOptions( +export function getComposerModelOptions( modelOption: CloudTaskConfigOption, ): MobileModelOption[] { return modelOption.options.map((option) => ({ @@ -33,24 +38,37 @@ export function getMobileModelOptions( })); } -export function getModelLabel( - modelOption: CloudTaskConfigOption, - value: string, -): string { - return ( - modelOption.options.find((option) => option.value === value)?.name ?? value - ); +export function getConfigOptionLabel( + options: ReadonlyArray<{ value: string; name: string }>, + value: string | undefined, +): string | undefined { + return options.find((option) => option.value === value)?.name ?? value; } -export function resolveAvailableModel( - modelOption: CloudTaskConfigOption, - value: string, -): string { - const selectedOption = modelOption.options.find( - (option) => option.value === value, - ); - if (selectedOption && !isRestrictedModelOption(selectedOption._meta)) { - return value; - } - return modelOption.currentValue; +export type ComposerPrimaryAction = + | "send" + | "stop" + | "mic" + | "mic-stop" + | "disabled"; + +export function resolveComposerPrimaryAction({ + hasContent, + disabled, + isRecording, + isTranscribing, + canStop, + allowSendWhileRunning, +}: { + hasContent: boolean; + disabled: boolean; + isRecording: boolean; + isTranscribing: boolean; + canStop: boolean; + allowSendWhileRunning: boolean; +}): ComposerPrimaryAction { + if (disabled || isTranscribing) return "disabled"; + if (canStop && (!allowSendWhileRunning || !hasContent)) return "stop"; + if (hasContent && !isRecording) return "send"; + return isRecording ? "mic-stop" : "mic"; } diff --git a/apps/mobile/src/features/tasks/skills/api.test.ts b/apps/mobile/src/features/tasks/skills/api.test.ts deleted file mode 100644 index 9c6a13f2a2..0000000000 --- a/apps/mobile/src/features/tasks/skills/api.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; - -const { mockFetch } = vi.hoisted(() => ({ - mockFetch: vi.fn(), -})); - -vi.mock("expo/fetch", () => ({ - fetch: mockFetch, -})); - -vi.mock("@/lib/api", () => ({ - getBaseUrl: () => "https://app.posthog.test", - getProjectId: () => 42, - authedFetch: (url: string, init?: RequestInit) => - mockFetch(url, { - ...init, - headers: { - Authorization: "Bearer token", - "Content-Type": "application/json", - ...((init?.headers as Record | undefined) ?? {}), - }, - }), -})); - -import { getSkillStoreSkill, getSkillStoreSkills } from "./api"; - -describe("skill store api", () => { - beforeEach(() => { - mockFetch.mockReset(); - }); - - it("parses paginated skill-list responses", async () => { - mockFetch.mockResolvedValueOnce( - new Response( - JSON.stringify({ - results: [ - { - name: "shared-daily-brief", - description: "Shared morning briefing starter", - }, - ], - }), - { status: 200 }, - ), - ); - - const skills = await getSkillStoreSkills(); - - expect(skills).toEqual([ - { - name: "shared-daily-brief", - description: "Shared morning briefing starter", - }, - ]); - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/environments/42/llm_skills/", - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: "Bearer token", - }), - }), - ); - }); - - it("encodes skill names for detail requests and returns the full body", async () => { - mockFetch.mockResolvedValueOnce( - new Response( - JSON.stringify({ - name: "shared/brief today", - description: "Shared briefing", - body: "Summarize what matters this morning.", - }), - { status: 200 }, - ), - ); - - const skill = await getSkillStoreSkill("shared/brief today"); - - expect(skill).toMatchObject({ - name: "shared/brief today", - body: "Summarize what matters this morning.", - }); - expect(mockFetch).toHaveBeenCalledWith( - "https://app.posthog.test/api/environments/42/llm_skills/name/shared%2Fbrief%20today/", - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: "Bearer token", - }), - }), - ); - }); -}); diff --git a/apps/mobile/src/features/tasks/skills/api.ts b/apps/mobile/src/features/tasks/skills/api.ts deleted file mode 100644 index 46584a4241..0000000000 --- a/apps/mobile/src/features/tasks/skills/api.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { authedFetch, getBaseUrl, getProjectId } from "@/lib/api"; -import type { SkillStoreListEntry, SkillStoreSkill } from "./types"; - -function skillStoreBaseUrl(): string { - const baseUrl = getBaseUrl(); - const projectId = getProjectId(); - return `${baseUrl}/api/environments/${projectId}/llm_skills`; -} - -async function readJsonOrThrow( - response: Response, - errorPrefix: string, -): Promise { - if (!response.ok) { - const data = (await response.json().catch(() => ({}))) as { - detail?: string; - }; - throw new Error(data.detail ?? `${errorPrefix}: ${response.statusText}`); - } - - return (await response.json()) as T; -} - -export async function getSkillStoreSkills(): Promise { - const response = await authedFetch(`${skillStoreBaseUrl()}/`); - - const data = await readJsonOrThrow< - SkillStoreListEntry[] | { results?: SkillStoreListEntry[] } - >(response, "Failed to fetch skills"); - - return Array.isArray(data) ? data : (data.results ?? []); -} - -export async function getSkillStoreSkill( - skillName: string, -): Promise { - const response = await authedFetch( - `${skillStoreBaseUrl()}/name/${encodeURIComponent(skillName)}/`, - ); - - return readJsonOrThrow(response, "Failed to fetch skill"); -} diff --git a/apps/mobile/src/features/tasks/skills/hooks.ts b/apps/mobile/src/features/tasks/skills/hooks.ts index c78a85cded..fcba3e5523 100644 --- a/apps/mobile/src/features/tasks/skills/hooks.ts +++ b/apps/mobile/src/features/tasks/skills/hooks.ts @@ -1,5 +1,5 @@ import { useQuery } from "@tanstack/react-query"; -import { getSkillStoreSkill, getSkillStoreSkills } from "./api"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; const skillStoreKeys = { all: ["skill-store"] as const, @@ -13,7 +13,7 @@ const skillStoreKeys = { export function useSkillStoreSkills() { return useQuery({ queryKey: skillStoreKeys.list(), - queryFn: getSkillStoreSkills, + queryFn: async () => (await getPostHogApiClient().listLlmSkills()) ?? [], staleTime: 5 * 60 * 1000, }); } @@ -21,7 +21,7 @@ export function useSkillStoreSkills() { export function useSkillStoreSkill(skillName: string | null) { return useQuery({ queryKey: skillStoreKeys.detail(skillName ?? ""), - queryFn: () => getSkillStoreSkill(skillName as string), + queryFn: () => getPostHogApiClient().getLlmSkillByName(skillName as string), enabled: !!skillName, staleTime: 5 * 60 * 1000, }); diff --git a/apps/mobile/src/features/tasks/stores/pendingPromptRecoveryStore.ts b/apps/mobile/src/features/tasks/stores/pendingPromptRecoveryStore.ts index 4e8894e353..bca210d849 100644 --- a/apps/mobile/src/features/tasks/stores/pendingPromptRecoveryStore.ts +++ b/apps/mobile/src/features/tasks/stores/pendingPromptRecoveryStore.ts @@ -1,3 +1,7 @@ +import { + capPendingPrompts, + listPendingPromptsNewestFirst, +} from "@posthog/core/tasks/pendingPrompts"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; @@ -5,8 +9,6 @@ import { createJSONStorage, persist } from "zustand/middleware"; // Persisted (unlike the transient in-memory echo store, whose entries vanish // the instant the live SSE copy lands) so a prompt survives the app being // killed mid-create and can be recovered on the next launch. -const MAX_RECOVERABLE_PROMPTS = 20; - export interface RecoverablePrompt { promptText: string; createdAt: number; @@ -20,19 +22,6 @@ interface PendingPromptRecoveryState { setHasHydrated: (hydrated: boolean) => void; } -function capToNewest( - byKey: Record, -): Record { - const keys = Object.keys(byKey); - if (keys.length <= MAX_RECOVERABLE_PROMPTS) return byKey; - const kept = keys - .sort((a, b) => byKey[b].createdAt - byKey[a].createdAt) - .slice(0, MAX_RECOVERABLE_PROMPTS); - const trimmed: Record = {}; - for (const key of kept) trimmed[key] = byKey[key]; - return trimmed; -} - export const usePendingPromptRecoveryStore = create()( persist( @@ -41,7 +30,7 @@ export const usePendingPromptRecoveryStore = hasHydrated: false, set: (key, promptText) => set((state) => ({ - byKey: capToNewest({ + byKey: capPendingPrompts({ ...state.byKey, [key]: { promptText, createdAt: Date.now() }, }), @@ -75,9 +64,9 @@ export const pendingPromptRecoveryStoreApi = { usePendingPromptRecoveryStore.getState().clear(key); }, getAllNewestFirst(): { key: string; prompt: RecoverablePrompt }[] { - return Object.entries(usePendingPromptRecoveryStore.getState().byKey) - .map(([key, prompt]) => ({ key, prompt })) - .sort((a, b) => b.prompt.createdAt - a.prompt.createdAt); + return listPendingPromptsNewestFirst( + usePendingPromptRecoveryStore.getState().byKey, + ); }, whenHydrated(): Promise { if (usePendingPromptRecoveryStore.getState().hasHydrated) { diff --git a/apps/mobile/src/features/tasks/stores/pendingTaskPromptStore.ts b/apps/mobile/src/features/tasks/stores/pendingTaskPromptStore.ts index e7face63f3..8d759d0086 100644 --- a/apps/mobile/src/features/tasks/stores/pendingTaskPromptStore.ts +++ b/apps/mobile/src/features/tasks/stores/pendingTaskPromptStore.ts @@ -1,3 +1,4 @@ +import { buildPendingPromptKey } from "@posthog/core/tasks/pendingPrompts"; import { create } from "zustand"; import type { SessionNotificationAttachment } from "../types"; @@ -79,8 +80,9 @@ export function generatePendingTaskKey(): string { typeof globalThis !== "undefined" ? (globalThis as { crypto?: { randomUUID?: () => string } }).crypto : undefined; - if (cryptoObj?.randomUUID) { - return cryptoObj.randomUUID(); - } - return `pending-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; + return buildPendingPromptKey( + cryptoObj?.randomUUID?.() ?? null, + Date.now(), + Math.random().toString(36).slice(2, 10), + ); } From 0d46bfbb2165d8b5a5eeaaeb9308ef2fdf578835 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:36:27 +0300 Subject: [PATCH 29/42] refactor(mobile): adopt shared presentation semantics Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/README.md | 23 +++- apps/mobile/src/app/(tabs)/inbox.tsx | 18 ++- apps/mobile/src/app/automation/[id].tsx | 2 +- apps/mobile/src/app/automation/create.tsx | 2 +- .../mobile/src/app/mcp-servers/add-custom.tsx | 2 +- apps/mobile/src/app/mcp-servers/index.tsx | 8 +- .../src/app/mcp-servers/installation/[id].tsx | 6 +- .../src/app/mcp-servers/template/[id].tsx | 4 +- .../features/chat/components/AgentMessage.tsx | 4 +- .../features/chat/components/ToolMessage.tsx | 15 ++- .../chat/utils/posthogExecDisplay.test.ts | 4 +- .../features/chat/utils/posthogExecDisplay.ts | 109 ---------------- .../chat/utils/thinkingMessages.test.ts | 23 ---- .../features/chat/utils/thinkingMessages.ts | 97 -------------- .../src/features/inbox/activityLog.test.ts | 6 +- apps/mobile/src/features/inbox/activityLog.ts | 82 ------------ .../inbox/components/ArchivedReportList.tsx | 3 +- .../inbox/components/ArtefactCommit.tsx | 2 +- .../inbox/components/ArtefactTaskRun.tsx | 2 +- .../features/inbox/components/DiffBlock.tsx | 2 +- .../inbox/components/ReportActivity.tsx | 10 +- .../features/inbox/hooks/useInboxReports.ts | 2 +- apps/mobile/src/features/inbox/utils.test.ts | 32 ++++- apps/mobile/src/features/inbox/utils.ts | 119 +---------------- .../features/mcp/components/McpAppHost.tsx | 11 +- .../features/mcp/components/McpServerRow.tsx | 11 +- apps/mobile/src/features/mcp/hooks.ts | 6 +- apps/mobile/src/features/mcp/mcpUiResource.ts | 6 + apps/mobile/src/features/mcp/oauth.ts | 14 +- .../features/mcp/sandbox/useMcpUiResource.ts | 3 +- apps/mobile/src/features/mcp/types.ts | 120 ------------------ .../features/mcp/utils/mcpToolName.test.ts | 48 ------- .../src/features/mcp/utils/mcpToolName.ts | 35 ----- .../tasks/components/AutomationDetail.tsx | 2 +- .../tasks/components/AutomationItem.tsx | 2 +- .../tasks/components/AutomationSkillCard.tsx | 4 +- .../tasks/components/TaskSessionView.test.tsx | 4 +- .../tasks/components/TaskSessionView.tsx | 8 +- .../features/tasks/skills/skillTemplateIds.ts | 16 --- .../mobile/src/features/tasks/skills/types.ts | 8 -- .../automationTemplatePresentation.test.ts | 2 +- .../utils/automationTemplatePresentation.ts | 25 ---- 42 files changed, 134 insertions(+), 768 deletions(-) delete mode 100644 apps/mobile/src/features/chat/utils/posthogExecDisplay.ts delete mode 100644 apps/mobile/src/features/chat/utils/thinkingMessages.test.ts delete mode 100644 apps/mobile/src/features/chat/utils/thinkingMessages.ts delete mode 100644 apps/mobile/src/features/inbox/activityLog.ts create mode 100644 apps/mobile/src/features/mcp/mcpUiResource.ts delete mode 100644 apps/mobile/src/features/mcp/types.ts delete mode 100644 apps/mobile/src/features/mcp/utils/mcpToolName.test.ts delete mode 100644 apps/mobile/src/features/mcp/utils/mcpToolName.ts delete mode 100644 apps/mobile/src/features/tasks/skills/skillTemplateIds.ts delete mode 100644 apps/mobile/src/features/tasks/skills/types.ts delete mode 100644 apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts diff --git a/apps/mobile/README.md b/apps/mobile/README.md index 6c9bbbedae..c68a3daad0 100644 --- a/apps/mobile/README.md +++ b/apps/mobile/README.md @@ -32,7 +32,7 @@ pnpm --filter @posthog/mobile start ### Feature Folders -Code is organized by feature in `src/features/`. Each feature is self-contained with its own components, hooks, stores, and API logic. +Code is organized by feature in `src/features/`. Features own native components, one-source hooks, and view state. They do not own copies of cloud contracts, transport, orchestration, or presentation rules. ``` src/features/ @@ -46,18 +46,31 @@ src/features/ │ ├── hooks/ │ ├── stores/ │ └── types.ts -├── conversations/ # PostHog AI conversation list & management -│ ├── api.ts +├── inbox/ # Native inbox rendering and query hooks │ ├── components/ │ ├── hooks/ │ └── stores/ -└── tasks/ # Task management - ├── api.ts +└── tasks/ # Native cloud-task rendering and host adapters ├── components/ ├── hooks/ + ├── services/ └── stores/ ``` +### Portability boundary + +Mobile and desktop use the same cloud-task architecture. New work must preserve these ownership rules: + +- `@posthog/shared` owns runtime contracts and Zod schemas. +- `@posthog/api-client` owns authenticated PostHog HTTPS transport and its request/response types. +- `@posthog/core` owns cloud-task orchestration and headless presentation decisions, including sessions, queues, permissions, models, repositories, inbox rules, and automation semantics. +- `apps/mobile` owns Expo lifecycle, React Native rendering, gestures, sheets, notifications, audio, secure storage, and small persisted view-state stores. +- `@posthog/ui` owns the DOM/Quill renderer and web view state. + +Do not add a mobile API facade, duplicate a shared type, or re-export a core helper through a mobile file. Import the owning package directly. If desktop and mobile need different visuals, add a headless descriptor or decision function to core and keep two thin renderers. + +Intentional host differences are limited to platform capabilities and view state. Mobile may persist native navigation state, cached picker snapshots, optimistic attachment echoes, and notification preferences; it must not implement retries, reconnection, transport parsing, task lifecycle, or cross-store decisions in those stores. + ### File-Based Routing Routes for the screens are defined by the file structure in `src/app/` using expo-router. diff --git a/apps/mobile/src/app/(tabs)/inbox.tsx b/apps/mobile/src/app/(tabs)/inbox.tsx index 1e64ab8df4..dd66e06c87 100644 --- a/apps/mobile/src/app/(tabs)/inbox.tsx +++ b/apps/mobile/src/app/(tabs)/inbox.tsx @@ -1,3 +1,4 @@ +import { buildInboxViewedProperties } from "@posthog/core/inbox/engagement"; import { INBOX_PIPELINE_STATUSES } from "@posthog/core/inbox/reportFiltering"; import type { SignalReport } from "@posthog/shared/domain-types"; import { useFocusEffect, useRouter } from "expo-router"; @@ -24,7 +25,6 @@ import { } from "@/features/inbox/stores/dismissedReportsStore"; import { useInboxFilterStore } from "@/features/inbox/stores/inboxFilterStore"; import { useInboxStore } from "@/features/inbox/stores/inboxStore"; -import { buildInboxViewedProperties } from "@/features/inbox/utils"; import { useIntegrations } from "@/features/tasks/hooks/useIntegrations"; import { ANALYTICS_EVENTS, useAnalytics } from "@/lib/analytics"; @@ -67,12 +67,16 @@ export default function InboxScreen() { viewedFiredForFocusRef.current = focusVersion; analytics.track( ANALYTICS_EVENTS.INBOX_VIEWED, - buildInboxViewedProperties(reports, totalCount, { - sourceProductFilter, - statusFilter, - suggestedReviewerFilter, - priorityFilter, - defaultStatusFilter: INBOX_PIPELINE_STATUSES, + buildInboxViewedProperties({ + visibleReports: reports, + totalCount, + filters: { + sourceProductFilter, + statusFilter, + suggestedReviewerFilter, + priorityFilter, + defaultStatusFilter: INBOX_PIPELINE_STATUSES, + }, }), ); }, [ diff --git a/apps/mobile/src/app/automation/[id].tsx b/apps/mobile/src/app/automation/[id].tsx index 55f9bd5c6d..159742297c 100644 --- a/apps/mobile/src/app/automation/[id].tsx +++ b/apps/mobile/src/app/automation/[id].tsx @@ -1,5 +1,6 @@ import { Text } from "@components/text"; import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; +import { parseSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useState } from "react"; import { @@ -20,7 +21,6 @@ import { useUpdateTaskAutomation, } from "@/features/tasks/hooks/useAutomations"; import { useTask } from "@/features/tasks/hooks/useTasks"; -import { parseSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds"; import { useThemeColors } from "@/lib/theme"; export default function AutomationDetailScreen() { diff --git a/apps/mobile/src/app/automation/create.tsx b/apps/mobile/src/app/automation/create.tsx index 1f4d662bff..1fe2c452c0 100644 --- a/apps/mobile/src/app/automation/create.tsx +++ b/apps/mobile/src/app/automation/create.tsx @@ -1,4 +1,5 @@ import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; +import { formatSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation"; import { getCalendars } from "expo-localization"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useMemo, useRef, useState } from "react"; @@ -14,7 +15,6 @@ import { Text } from "@/components/text"; import { AutomationForm } from "@/features/tasks/components/AutomationForm"; import { useCreateTaskAutomation } from "@/features/tasks/hooks/useAutomations"; import { useSkillStoreSkill } from "@/features/tasks/skills/hooks"; -import { formatSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; diff --git a/apps/mobile/src/app/mcp-servers/add-custom.tsx b/apps/mobile/src/app/mcp-servers/add-custom.tsx index fff6f5e3bf..cb48d1b68e 100644 --- a/apps/mobile/src/app/mcp-servers/add-custom.tsx +++ b/apps/mobile/src/app/mcp-servers/add-custom.tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import type { McpAuthType } from "@posthog/api-client/types"; import { router } from "expo-router"; import { Lock } from "phosphor-react-native"; import { useState } from "react"; @@ -14,7 +15,6 @@ import { import { FloatingMcpHeader } from "@/features/mcp/components/FloatingMcpHeader"; import { useMcpInstallations } from "@/features/mcp/hooks"; import { installCustomWithOAuth } from "@/features/mcp/oauth"; -import type { McpAuthType } from "@/features/mcp/types"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; import { useThemeColors } from "@/lib/theme"; diff --git a/apps/mobile/src/app/mcp-servers/index.tsx b/apps/mobile/src/app/mcp-servers/index.tsx index e566e7ef75..680609d400 100644 --- a/apps/mobile/src/app/mcp-servers/index.tsx +++ b/apps/mobile/src/app/mcp-servers/index.tsx @@ -1,4 +1,8 @@ import { Text } from "@components/text"; +import type { + McpRecommendedServer, + McpServerInstallation, +} from "@posthog/api-client/types"; import { useRouter } from "expo-router"; import { MagnifyingGlass, Plus, PuzzlePiece } from "phosphor-react-native"; import { useMemo, useState } from "react"; @@ -17,10 +21,6 @@ import { recommendedToRowProps, } from "@/features/mcp/components/McpServerRow"; import { useMcpInstallations, useMcpMarketplace } from "@/features/mcp/hooks"; -import type { - McpRecommendedServer, - McpServerInstallation, -} from "@/features/mcp/types"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; diff --git a/apps/mobile/src/app/mcp-servers/installation/[id].tsx b/apps/mobile/src/app/mcp-servers/installation/[id].tsx index d0dd6fcd02..837edb91c9 100644 --- a/apps/mobile/src/app/mcp-servers/installation/[id].tsx +++ b/apps/mobile/src/app/mcp-servers/installation/[id].tsx @@ -1,4 +1,6 @@ import { Text } from "@components/text"; +import type { McpApprovalState } from "@posthog/api-client/types"; +import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { router, useLocalSearchParams } from "expo-router"; import { ArrowsClockwise, @@ -28,8 +30,6 @@ import { } from "@/features/mcp/hooks"; import { reauthorizeInstallation } from "@/features/mcp/oauth"; import { getMcpConnectionManager } from "@/features/mcp/service"; -import type { McpApprovalState } from "@/features/mcp/types"; -import { isStdioServer } from "@/features/mcp/types"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; import { useThemeColors } from "@/lib/theme"; @@ -75,7 +75,7 @@ export default function McpInstallationDetailScreen() { ); } - const stdio = isStdioServer(installation); + const stdio = isStdioMcpServer(installation); const handleEnabledChange = (enabled: boolean) => { updateMutation.mutate({ diff --git a/apps/mobile/src/app/mcp-servers/template/[id].tsx b/apps/mobile/src/app/mcp-servers/template/[id].tsx index 78e138c19a..348c7a488b 100644 --- a/apps/mobile/src/app/mcp-servers/template/[id].tsx +++ b/apps/mobile/src/app/mcp-servers/template/[id].tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { router, useLocalSearchParams } from "expo-router"; import { Lock, Warning } from "phosphor-react-native"; import { useMemo, useState } from "react"; @@ -17,7 +18,6 @@ import { useMcpMarketplace, } from "@/features/mcp/hooks"; import { installTemplateWithOAuth } from "@/features/mcp/oauth"; -import { isStdioServer } from "@/features/mcp/types"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; import { openExternalUrl } from "@/lib/openExternalUrl"; @@ -71,7 +71,7 @@ export default function McpTemplateDetailScreen() { ); } - const stdio = isStdioServer(template); + const stdio = isStdioMcpServer(template); const handleInstall = async () => { if (!template) return; diff --git a/apps/mobile/src/features/chat/components/AgentMessage.tsx b/apps/mobile/src/features/chat/components/AgentMessage.tsx index e6fa274cb6..e767f2f43a 100644 --- a/apps/mobile/src/features/chat/components/AgentMessage.tsx +++ b/apps/mobile/src/features/chat/components/AgentMessage.tsx @@ -1,10 +1,10 @@ +import { pickThinkingActivity } from "@posthog/core/sessions/thinkingActivities"; import { Brain } from "phosphor-react-native"; import { useState } from "react"; import { Pressable, Text, View } from "react-native"; import { formatRelativeTime } from "@/lib/format"; import { useThemeColors } from "@/lib/theme"; import { usePeriodicRerender } from "../hooks/usePeriodicRerender"; -import { getRandomThinkingMessage } from "../utils/thinkingMessages"; import { CopyButton } from "./CopyButton"; import { MarkdownText } from "./MarkdownText"; import { ToolMessage } from "./ToolMessage"; @@ -110,7 +110,7 @@ export function AgentMessage({ {isLoading && !content && !thinkingText && ( - {getRandomThinkingMessage()} + {pickThinkingActivity(Math.random())}... )} diff --git a/apps/mobile/src/features/chat/components/ToolMessage.tsx b/apps/mobile/src/features/chat/components/ToolMessage.tsx index 3859504777..2555a211e1 100644 --- a/apps/mobile/src/features/chat/components/ToolMessage.tsx +++ b/apps/mobile/src/features/chat/components/ToolMessage.tsx @@ -1,3 +1,9 @@ +import { + formatPosthogExecBody, + getPostHogExecDisplay, + isPostHogExecTool, +} from "@posthog/core/sessions/posthogExecDisplay"; +import { parseMcpToolName } from "@posthog/shared"; import { useRouter } from "expo-router"; import { ArrowsClockwise, @@ -22,13 +28,7 @@ import { TouchableOpacity, View, } from "react-native"; -import { - formatPosthogExecBody, - getPostHogExecDisplay, - isPostHogExecTool, -} from "@/features/chat/utils/posthogExecDisplay"; import { McpAppHost } from "@/features/mcp/components/McpAppHost"; -import { isMcpToolName } from "@/features/mcp/utils/mcpToolName"; import { getColorForClass, highlightCode, @@ -942,7 +942,8 @@ export function ToolMessage({ // MCP App tools render via the WebView host — skip PostHog exec (which has // its own renderer above) and only kick in once the tool finished or while // it's running so we don't show empty WebView shells for pending tools. - const isMcpAppTool = !isPostHogExec && isMcpToolName(effectiveToolName); + const isMcpAppTool = + !isPostHogExec && parseMcpToolName(effectiveToolName) !== undefined; if (isMcpAppTool && !isPending) { return ( diff --git a/apps/mobile/src/features/chat/utils/posthogExecDisplay.test.ts b/apps/mobile/src/features/chat/utils/posthogExecDisplay.test.ts index c2a81f3e0f..ca08b80c33 100644 --- a/apps/mobile/src/features/chat/utils/posthogExecDisplay.test.ts +++ b/apps/mobile/src/features/chat/utils/posthogExecDisplay.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it } from "vitest"; import { formatPosthogExecBody, getPostHogExecDisplay, isPostHogExecTool, -} from "./posthogExecDisplay"; +} from "@posthog/core/sessions/posthogExecDisplay"; +import { describe, expect, it } from "vitest"; describe("isPostHogExecTool", () => { it("matches the bare posthog exec tool", () => { diff --git a/apps/mobile/src/features/chat/utils/posthogExecDisplay.ts b/apps/mobile/src/features/chat/utils/posthogExecDisplay.ts deleted file mode 100644 index d46e759f25..0000000000 --- a/apps/mobile/src/features/chat/utils/posthogExecDisplay.ts +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Mirrors the desktop PostHog MCP exec display logic so mobile unwraps the - * dispatched sub-tool instead of showing the raw `exec` transport wrapper. - * - * Supported verbs: - * tools - * search - * info - * schema [field_path] - * call [--json] - */ - -const POSTHOG_EXEC_TOOL_RE = /^mcp__(?:plugin_)?posthog(?:_[^_]+)*__exec$/; - -const POSTHOG_VERB_RE = - /^\s*(tools|search|info|schema|call)(?:\s+([\s\S]*))?\s*$/; -const POSTHOG_CALL_BODY_RE = /^(?:--json\s+)?([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; -const POSTHOG_TOOL_NAME_RE = /^([a-zA-Z0-9_-]+)\s*([\s\S]*)$/; - -export interface PostHogExecDisplay { - label: string; - input?: string; -} - -export function isPostHogExecTool(toolName: string): boolean { - return POSTHOG_EXEC_TOOL_RE.test(toolName); -} - -export function getPostHogExecDisplay( - toolInput: unknown, -): PostHogExecDisplay | null { - if (!toolInput || typeof toolInput !== "object") return null; - const obj = toolInput as { command?: unknown; input?: unknown }; - - if (typeof obj.command !== "string") return null; - const verbMatch = obj.command.match(POSTHOG_VERB_RE); - if (!verbMatch) return null; - - const verb = verbMatch[1] as "tools" | "search" | "info" | "schema" | "call"; - const rest = (verbMatch[2] ?? "").trim(); - const explicitInput = readExplicitInput(obj.input); - - switch (verb) { - case "tools": - return { label: "List tools", input: undefined }; - - case "search": - return { - label: "Search tools", - input: explicitInput ?? (rest.length > 0 ? rest : undefined), - }; - - case "info": - return rest.length > 0 - ? { label: `Read ${rest}`, input: undefined } - : { label: "Read tool", input: undefined }; - - case "schema": { - const match = rest.match(POSTHOG_TOOL_NAME_RE); - if (!match) return { label: "Inspect schema", input: undefined }; - const subTool = match[1]; - const fieldPath = (match[2] ?? "").trim(); - const path = - explicitInput ?? (fieldPath.length > 0 ? fieldPath : undefined); - return { - label: path - ? `Inspect ${subTool}.${path}` - : `Inspect ${subTool} fields`, - input: undefined, - }; - } - - case "call": { - const match = rest.match(POSTHOG_CALL_BODY_RE); - if (!match) return null; - const subTool = match[1]; - const args = (match[2] ?? "").trim(); - return { - label: subTool, - input: explicitInput ?? (args.length > 0 ? args : undefined), - }; - } - } -} - -function readExplicitInput(value: unknown): string | undefined { - if (value === undefined || value === null) return undefined; - if (typeof value === "string") return value.trim() || undefined; - try { - return JSON.stringify(value); - } catch { - return undefined; - } -} - -export function formatPosthogExecBody( - input: string | undefined, -): string | undefined { - if (!input) return undefined; - try { - const parsed = JSON.parse(input); - if (parsed && typeof parsed === "object") { - return JSON.stringify(parsed, null, 2); - } - } catch { - // Not JSON; show the raw input. - } - return input; -} diff --git a/apps/mobile/src/features/chat/utils/thinkingMessages.test.ts b/apps/mobile/src/features/chat/utils/thinkingMessages.test.ts deleted file mode 100644 index de528d1560..0000000000 --- a/apps/mobile/src/features/chat/utils/thinkingMessages.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import { - getRandomThinkingActivity, - getRandomThinkingMessage, - THINKING_MESSAGES, -} from "./thinkingMessages"; - -describe("thinkingMessages", () => { - it("includes the whimsical cloud-run loading messages from desktop", () => { - expect(THINKING_MESSAGES).toContain("Kerfuffling"); - expect(THINKING_MESSAGES).toContain("Flibbertigibbeting"); - expect(THINKING_MESSAGES).toContain("Discombobulating"); - }); - - it("returns a bare activity label and a message variant with ellipsis", () => { - const randomSpy = vi.spyOn(Math, "random").mockReturnValue(0); - - expect(getRandomThinkingActivity()).toBe("Booping"); - expect(getRandomThinkingMessage()).toBe("Booping..."); - - randomSpy.mockRestore(); - }); -}); diff --git a/apps/mobile/src/features/chat/utils/thinkingMessages.ts b/apps/mobile/src/features/chat/utils/thinkingMessages.ts deleted file mode 100644 index 2eee2a1de4..0000000000 --- a/apps/mobile/src/features/chat/utils/thinkingMessages.ts +++ /dev/null @@ -1,97 +0,0 @@ -// Random thinking messages displayed while AI is generating -// Based on posthog/frontend/src/scenes/max/utils/thinkingMessages.ts - -export const THINKING_MESSAGES = [ - "Booping", - "Crunching", - "Digging", - "Fetching", - "Inferring", - "Indexing", - "Juggling", - "Noodling", - "Peeking", - "Percolating", - "Poking", - "Pondering", - "Scanning", - "Scrambling", - "Sifting", - "Sniffing", - "Spelunking", - "Tinkering", - "Unraveling", - "Decoding", - "Trekking", - "Sorting", - "Trimming", - "Mulling", - "Surfacing", - "Rummaging", - "Scouting", - "Scouring", - "Threading", - "Hunting", - "Swizzling", - "Grokking", - "Hedging", - "Scheming", - "Unfurling", - "Puzzling", - "Dissecting", - "Stacking", - "Snuffling", - "Hashing", - "Clustering", - "Teasing", - "Cranking", - "Merging", - "Snooping", - "Rewiring", - "Bundling", - "Linking", - "Mapping", - "Tickling", - "Flicking", - "Hopping", - "Rolling", - "Zipping", - "Twisting", - "Blooming", - "Sparking", - "Nesting", - "Wiring", - "Snipping", - "Zoning", - "Tracing", - "Warping", - "Twinkling", - "Flipping", - "Priming", - "Snagging", - "Scuttling", - "Framing", - "Sharpening", - "Flibbertigibbeting", - "Kerfuffling", - "Dithering", - "Discombobulating", - "Rambling", - "Befuddling", - "Waffling", - "Muckling", - "Hobnobbing", - "Galumphing", - "Puttering", - "Whiffling", - "Thinking", -]; - -export function getRandomThinkingActivity(): string { - const randomIndex = Math.floor(Math.random() * THINKING_MESSAGES.length); - return THINKING_MESSAGES[randomIndex]; -} - -export function getRandomThinkingMessage(): string { - return `${getRandomThinkingActivity()}...`; -} diff --git a/apps/mobile/src/features/inbox/activityLog.test.ts b/apps/mobile/src/features/inbox/activityLog.test.ts index 8ba0a681fe..1032197c50 100644 --- a/apps/mobile/src/features/inbox/activityLog.test.ts +++ b/apps/mobile/src/features/inbox/activityLog.test.ts @@ -1,12 +1,12 @@ -import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; -import { describe, expect, it } from "vitest"; import { attributionLabel, parseDiffLines, selectActivityArtefacts, shortSha, taskRunLabel, -} from "./activityLog"; +} from "@posthog/core/inbox/activityLog"; +import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; +import { describe, expect, it } from "vitest"; function commit(id: string, createdAt: string): AnySignalReportArtefact { return { diff --git a/apps/mobile/src/features/inbox/activityLog.ts b/apps/mobile/src/features/inbox/activityLog.ts deleted file mode 100644 index cb11aefa22..0000000000 --- a/apps/mobile/src/features/inbox/activityLog.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; - -export type ActivityArtefact = Extract< - AnySignalReportArtefact, - { type: "commit" | "task_run" } ->; - -export function selectActivityArtefacts( - artefacts: AnySignalReportArtefact[], -): ActivityArtefact[] { - return artefacts - .filter( - (a): a is ActivityArtefact => - a.type === "commit" || a.type === "task_run", - ) - .sort((a, b) => a.created_at.localeCompare(b.created_at)); -} - -export function shortSha(sha: string): string { - return sha.slice(0, 12); -} - -const SIGNALS_TYPE_LABELS: Record = { - research: "Research", - implementation: "Implementation", - repo_selection: "Repo selection", -}; - -function humanizeIdentifier(value: string): string { - const spaced = value.replace(/[_-]+/g, " ").trim(); - return spaced.charAt(0).toUpperCase() + spaced.slice(1); -} - -export function taskRunLabel(content: { - product: string; - type: string; -}): string { - if (content.product === "signals") { - return ( - SIGNALS_TYPE_LABELS[content.type] ?? humanizeIdentifier(content.type) - ); - } - return humanizeIdentifier(content.type); -} - -export function attributionLabel(artefact: { - created_by?: { first_name?: string; email: string } | null; - task_id?: string | null; -}): string | null { - if (artefact.created_by) { - return artefact.created_by.first_name?.trim() || artefact.created_by.email; - } - if (artefact.task_id) { - return "agent"; - } - return null; -} - -type DiffLineKind = "add" | "del" | "hunk" | "context"; - -interface DiffLine { - text: string; - kind: DiffLineKind; -} - -export function parseDiffLines(diff: string): DiffLine[] { - return diff - .replace(/\n$/, "") - .split("\n") - .map((text) => { - if (text.startsWith("+") && !text.startsWith("+++")) { - return { text, kind: "add" as const }; - } - if (text.startsWith("-") && !text.startsWith("---")) { - return { text, kind: "del" as const }; - } - if (text.startsWith("@@")) { - return { text, kind: "hunk" as const }; - } - return { text, kind: "context" as const }; - }); -} diff --git a/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx b/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx index a0aed9bc98..72de972737 100644 --- a/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx +++ b/apps/mobile/src/features/inbox/components/ArchivedReportList.tsx @@ -1,4 +1,5 @@ import { Text } from "@components/text"; +import { isRestorableReport } from "@posthog/core/inbox/reportMembership"; import { inboxStatusLabel } from "@posthog/core/inbox/reportPresentation"; import { dismissalReasonLabel } from "@posthog/shared"; import type { SignalReport } from "@posthog/shared/domain-types"; @@ -14,7 +15,7 @@ import { } from "react-native"; import { useThemeColors } from "@/lib/theme"; import { useArchivedReports, useRestoreReport } from "../hooks/useInboxReports"; -import { formatReportTimestamp, isRestorableReport } from "../utils"; +import { formatReportTimestamp } from "../utils"; interface ArchivedReportListProps { onReportPress?: (report: SignalReport) => void; diff --git a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx index 19f4cc2510..3da3aa2355 100644 --- a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx +++ b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx @@ -1,10 +1,10 @@ import { Text } from "@components/text"; +import { shortSha } from "@posthog/core/inbox/activityLog"; import type { CommitContent } from "@posthog/shared/domain-types"; import { CaretDown, CaretRight } from "phosphor-react-native"; import { useState } from "react"; import { ActivityIndicator, Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; -import { shortSha } from "../activityLog"; import { useCommitDiff } from "../hooks/useInboxReports"; import { DiffBlock } from "./DiffBlock"; diff --git a/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx b/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx index 2c2b004a58..f7d5f2d154 100644 --- a/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx +++ b/apps/mobile/src/features/inbox/components/ArtefactTaskRun.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; +import { taskRunLabel } from "@posthog/core/inbox/activityLog"; import type { TaskRunArtefactContent } from "@posthog/shared/domain-types"; import { useRouter } from "expo-router"; import { CaretRight } from "phosphor-react-native"; import { Pressable, View } from "react-native"; import { useTask } from "@/features/tasks"; import { useThemeColors } from "@/lib/theme"; -import { taskRunLabel } from "../activityLog"; export function ArtefactTaskRun({ content, diff --git a/apps/mobile/src/features/inbox/components/DiffBlock.tsx b/apps/mobile/src/features/inbox/components/DiffBlock.tsx index 4bf6bbc8c5..f6329cbd3b 100644 --- a/apps/mobile/src/features/inbox/components/DiffBlock.tsx +++ b/apps/mobile/src/features/inbox/components/DiffBlock.tsx @@ -1,6 +1,6 @@ import { Text } from "@components/text"; +import { parseDiffLines } from "@posthog/core/inbox/activityLog"; import { ScrollView, View } from "react-native"; -import { parseDiffLines } from "../activityLog"; const LINE_CLASS: Record = { add: "bg-status-success/15 text-status-success", diff --git a/apps/mobile/src/features/inbox/components/ReportActivity.tsx b/apps/mobile/src/features/inbox/components/ReportActivity.tsx index a20d721bd4..d33b501b2f 100644 --- a/apps/mobile/src/features/inbox/components/ReportActivity.tsx +++ b/apps/mobile/src/features/inbox/components/ReportActivity.tsx @@ -1,15 +1,15 @@ import { Text } from "@components/text"; +import { + type ActivityArtefact, + attributionLabel, + selectActivityArtefacts, +} from "@posthog/core/inbox/activityLog"; import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; import { ClockCounterClockwise } from "phosphor-react-native"; import { useMemo } from "react"; import { View } from "react-native"; import { formatRelativeTime } from "@/lib/format"; import { useThemeColors } from "@/lib/theme"; -import { - type ActivityArtefact, - attributionLabel, - selectActivityArtefacts, -} from "../activityLog"; import { ArtefactCommit } from "./ArtefactCommit"; import { ArtefactTaskRun } from "./ArtefactTaskRun"; diff --git a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts index e262300bf9..5a9229edc9 100644 --- a/apps/mobile/src/features/inbox/hooks/useInboxReports.ts +++ b/apps/mobile/src/features/inbox/hooks/useInboxReports.ts @@ -7,6 +7,7 @@ import { INBOX_DISMISSED_STATUS_FILTER, INBOX_REFETCH_INTERVAL_MS, } from "@posthog/core/inbox/reportFiltering"; +import { isRestorableReport } from "@posthog/core/inbox/reportMembership"; import type { DismissalReasonOptionValue } from "@posthog/shared"; import type { AvailableSuggestedReviewersResponse, @@ -31,7 +32,6 @@ import { useMemo } from "react"; import { useAuthStore } from "@/features/auth"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { useInboxFilterStore } from "../stores/inboxFilterStore"; -import { isRestorableReport } from "../utils"; export const inboxKeys = { all: ["inbox", "signal-reports"] as const, diff --git a/apps/mobile/src/features/inbox/utils.test.ts b/apps/mobile/src/features/inbox/utils.test.ts index ce8b2b43af..050f29041f 100644 --- a/apps/mobile/src/features/inbox/utils.test.ts +++ b/apps/mobile/src/features/inbox/utils.test.ts @@ -32,6 +32,24 @@ function signal(source_product: string, source_type: string): Signal { }; } +function buildMobileInboxViewedProperties( + reports: SignalReport[], + totalCount: number, + filters: { + sourceProductFilter: string[]; + statusFilter: readonly SignalReportStatus[]; + suggestedReviewerFilter: string[]; + priorityFilter: string[]; + defaultStatusFilter: readonly SignalReportStatus[]; + }, +) { + return buildInboxViewedProperties({ + visibleReports: reports, + totalCount, + filters, + }); +} + function makeReport( partial: Partial & Pick, ): SignalReport { @@ -88,7 +106,7 @@ describe("formatSignalReportSummaryMarkdown", () => { describe("buildInboxViewedProperties", () => { it("emits zero counts for an empty list", () => { - const props = buildInboxViewedProperties([], 0, { + const props = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: [], statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], @@ -137,7 +155,7 @@ describe("buildInboxViewedProperties", () => { makeReport({ id: "4", status: "failed" }), ]; - const props = buildInboxViewedProperties(reports, 4, { + const props = buildMobileInboxViewedProperties(reports, 4, { sourceProductFilter: [], statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], @@ -158,7 +176,7 @@ describe("buildInboxViewedProperties", () => { }); it("marks filters active when any of status/source/reviewer/priority differs from defaults", () => { - const narrowed = buildInboxViewedProperties([], 0, { + const narrowed = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: [], statusFilter: ["ready"], suggestedReviewerFilter: [], @@ -168,7 +186,7 @@ describe("buildInboxViewedProperties", () => { expect(narrowed.has_active_filters).toBe(true); expect(narrowed.status_filter_count).toBe(1); - const sourced = buildInboxViewedProperties([], 0, { + const sourced = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: ["error_tracking"], statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], @@ -178,7 +196,7 @@ describe("buildInboxViewedProperties", () => { expect(sourced.has_active_filters).toBe(true); expect(sourced.source_product_filter).toEqual(["error_tracking"]); - const reviewer = buildInboxViewedProperties([], 0, { + const reviewer = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: [], statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: ["uuid-1"], @@ -187,7 +205,7 @@ describe("buildInboxViewedProperties", () => { }); expect(reviewer.has_active_filters).toBe(true); - const prioritized = buildInboxViewedProperties([], 0, { + const prioritized = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: [], statusFilter: INBOX_PIPELINE_STATUSES, suggestedReviewerFilter: [], @@ -198,7 +216,7 @@ describe("buildInboxViewedProperties", () => { }); it("treats a reordered default status set as not filtered", () => { - const props = buildInboxViewedProperties([], 0, { + const props = buildMobileInboxViewedProperties([], 0, { sourceProductFilter: [], statusFilter: [...INBOX_PIPELINE_STATUSES].reverse(), suggestedReviewerFilter: [], diff --git a/apps/mobile/src/features/inbox/utils.ts b/apps/mobile/src/features/inbox/utils.ts index 13a8e4c80b..912bcd7104 100644 --- a/apps/mobile/src/features/inbox/utils.ts +++ b/apps/mobile/src/features/inbox/utils.ts @@ -2,14 +2,8 @@ import { EXTERNAL_INBOX_SOURCE_BY_PRODUCT, type SourceProduct, } from "@posthog/shared"; -import type { - Signal, - SignalReport, - SignalReportPriority, - SignalReportStatus, -} from "@posthog/shared/domain-types"; +import type { Signal } from "@posthog/shared/domain-types"; import { differenceInHours, format, formatDistanceToNow } from "date-fns"; -import type { InboxViewedProperties } from "@/lib/analytics"; const ERROR_TRACKING_TYPE_LABELS: Record = { issue_created: "New issue", @@ -47,120 +41,9 @@ export function sourceLine(signal: Signal): string { const product = warehouseSource?.label ?? source_product.replace(/_/g, " "); return `${product} · ${source_type.replace(/_/g, " ")}`; } - /** Relative time for the last day, absolute "MMM d" beyond it. */ export function formatReportTimestamp(date: Date): string { return differenceInHours(new Date(), date) < 24 ? formatDistanceToNow(date, { addSuffix: true }) : format(date, "MMM d"); } - -/** - * Archive membership: `suppressed` (user-archived) and `resolved` (PR merged). - * Only `suppressed` is restorable; `resolved` is terminal, shown for reference. - */ -export function isRestorableReport( - report: Pick, -): boolean { - return report.status === "suppressed"; -} - -/** - * Returns only reports that are actionable for the tinder-like card deck: - * ready, immediately actionable, not already addressed. - */ -export function getActionableReports(reports: SignalReport[]): SignalReport[] { - return reports.filter( - (r) => - r.status === "ready" && - r.actionability === "immediately_actionable" && - !r.already_addressed, - ); -} - -interface InboxViewedFilterState { - sourceProductFilter: string[]; - statusFilter: readonly SignalReportStatus[]; - suggestedReviewerFilter: string[]; - priorityFilter: SignalReportPriority[]; - /** Default status filter as defined in the filter store, used to detect whether the user has narrowed it. */ - defaultStatusFilter: readonly SignalReportStatus[]; -} - -/** - * Build the property payload for the `Inbox viewed` analytics event. - * - * Mirrors packages/ui/src/features/inbox/components/InboxSignalsTab.tsx so - * desktop and mobile send the same shape into PostHog. - */ -export function buildInboxViewedProperties( - reports: SignalReport[], - totalCount: number, - filters: InboxViewedFilterState, -): InboxViewedProperties { - const priorityCounts = { - P0: 0, - P1: 0, - P2: 0, - P3: 0, - P4: 0, - unknown: 0, - }; - const actionabilityCounts = { - immediately_actionable: 0, - requires_human_input: 0, - not_actionable: 0, - unknown: 0, - }; - let readyCount = 0; - for (const r of reports) { - if (r.status === "ready") readyCount += 1; - const p = r.priority; - if (p === "P0" || p === "P1" || p === "P2" || p === "P3" || p === "P4") { - priorityCounts[p] += 1; - } else { - priorityCounts.unknown += 1; - } - const a = r.actionability; - if ( - a === "immediately_actionable" || - a === "requires_human_input" || - a === "not_actionable" - ) { - actionabilityCounts[a] += 1; - } else { - actionabilityCounts.unknown += 1; - } - } - - const statusFiltered = - filters.statusFilter.length !== filters.defaultStatusFilter.length || - filters.statusFilter.some((s) => !filters.defaultStatusFilter.includes(s)); - const hasActiveFilters = - statusFiltered || - filters.sourceProductFilter.length > 0 || - filters.suggestedReviewerFilter.length > 0 || - filters.priorityFilter.length > 0; - - return { - report_count: reports.length, - total_count: totalCount, - ready_count: readyCount, - has_active_filters: hasActiveFilters, - source_product_filter: filters.sourceProductFilter, - status_filter_count: filters.statusFilter.length, - is_empty: totalCount === 0, - priority_p0_count: priorityCounts.P0, - priority_p1_count: priorityCounts.P1, - priority_p2_count: priorityCounts.P2, - priority_p3_count: priorityCounts.P3, - priority_p4_count: priorityCounts.P4, - priority_unknown_count: priorityCounts.unknown, - actionability_immediately_actionable_count: - actionabilityCounts.immediately_actionable, - actionability_requires_human_input_count: - actionabilityCounts.requires_human_input, - actionability_not_actionable_count: actionabilityCounts.not_actionable, - actionability_unknown_count: actionabilityCounts.unknown, - }; -} diff --git a/apps/mobile/src/features/mcp/components/McpAppHost.tsx b/apps/mobile/src/features/mcp/components/McpAppHost.tsx index e1f7638e7b..a92a6966e4 100644 --- a/apps/mobile/src/features/mcp/components/McpAppHost.tsx +++ b/apps/mobile/src/features/mcp/components/McpAppHost.tsx @@ -1,7 +1,7 @@ import { Text } from "@components/text"; import type { McpUiDisplayMode } from "@modelcontextprotocol/ext-apps/app-bridge"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; -import { isSafeExternalUrl } from "@posthog/shared"; +import { isSafeExternalUrl, parseMcpToolName } from "@posthog/shared"; import * as WebBrowser from "expo-web-browser"; import { ArrowsIn, ArrowsOut, Warning } from "phosphor-react-native"; import { useCallback, useMemo, useRef, useState } from "react"; @@ -22,7 +22,6 @@ import { sandboxProxyHtml } from "../sandbox/sandboxProxyHtml"; import { useMcpUiResource } from "../sandbox/useMcpUiResource"; import { type Phase, useMobileAppBridge } from "../sandbox/useMobileAppBridge"; import { getMcpConnectionManager } from "../service"; -import { parseMcpToolName } from "../utils/mcpToolName"; interface McpAppHostProps { /** Raw tool name from the agent — `mcp____`. */ @@ -60,14 +59,12 @@ export function McpAppHost(props: McpAppHostProps) { const installations = useMcpInstallations(); const installation = useMemo(() => { if (!parsed) return null; - return ( - installations.data?.find((i) => i.name === parsed.serverName) ?? null - ); + return installations.data?.find((i) => i.name === parsed.server) ?? null; }, [installations.data, parsed]); const uiResource = useMcpUiResource({ installation, - toolName: parsed?.toolName ?? "", + toolName: parsed?.tool ?? "", }); const webViewRef = useRef(null); @@ -122,7 +119,7 @@ export function McpAppHost(props: McpAppHostProps) { const { handleWebViewMessage } = useMobileAppBridge({ webViewRef, uiResource: uiResource.data?.resource ?? null, - serverName: parsed?.serverName ?? "", + serverName: parsed?.server ?? "", toolDefinition: uiResource.data?.tool ?? null, toolInput: props.toolArgs ?? null, existingToolResult: diff --git a/apps/mobile/src/features/mcp/components/McpServerRow.tsx b/apps/mobile/src/features/mcp/components/McpServerRow.tsx index 0cfd05775f..7b0a4b9359 100644 --- a/apps/mobile/src/features/mcp/components/McpServerRow.tsx +++ b/apps/mobile/src/features/mcp/components/McpServerRow.tsx @@ -1,10 +1,13 @@ import { Text } from "@components/text"; +import type { + McpRecommendedServer, + McpServerInstallation, +} from "@posthog/api-client/types"; +import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { CaretRight, Lock, Warning } from "phosphor-react-native"; import type { ReactNode } from "react"; import { Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; -import type { McpRecommendedServer, McpServerInstallation } from "../types"; -import { isStdioServer } from "../types"; import { ServerIcon } from "./ServerIcon"; interface McpServerRowProps { @@ -121,7 +124,7 @@ export function recommendedToRowProps( title: template.name, description: template.description, authType: template.auth_type, - isStdio: isStdioServer(template), + isStdio: isStdioMcpServer(template), installed: installedNames.has(template.name), iconDomain: template.icon_domain, serverUrl: template.url, @@ -137,7 +140,7 @@ export function installationToRowProps( title: installation.display_name || installation.name, subtitle: installation.url, authType: installation.auth_type, - isStdio: isStdioServer(installation), + isStdio: isStdioMcpServer(installation), needsReauth: installation.needs_reauth, installed: true, iconDomain: installation.icon_domain, diff --git a/apps/mobile/src/features/mcp/hooks.ts b/apps/mobile/src/features/mcp/hooks.ts index 4ecf708d61..6d6f07756e 100644 --- a/apps/mobile/src/features/mcp/hooks.ts +++ b/apps/mobile/src/features/mcp/hooks.ts @@ -1,11 +1,11 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { getPostHogApiClient } from "@/lib/posthogApiClient"; import type { InstallCustomMcpServerOptions, InstallMcpTemplateOptions, McpApprovalState, UpdateMcpServerInstallationOptions, -} from "./types"; +} from "@posthog/api-client/types"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; const mcpKeys = { all: ["mcp"] as const, diff --git a/apps/mobile/src/features/mcp/mcpUiResource.ts b/apps/mobile/src/features/mcp/mcpUiResource.ts new file mode 100644 index 0000000000..f9e934bfa6 --- /dev/null +++ b/apps/mobile/src/features/mcp/mcpUiResource.ts @@ -0,0 +1,6 @@ +export interface McpUiResource { + uri: string; + html: string; + csp?: Record; + permissions?: Record>; +} diff --git a/apps/mobile/src/features/mcp/oauth.ts b/apps/mobile/src/features/mcp/oauth.ts index 0c91a6cdf1..e687baa113 100644 --- a/apps/mobile/src/features/mcp/oauth.ts +++ b/apps/mobile/src/features/mcp/oauth.ts @@ -1,13 +1,13 @@ -import * as Linking from "expo-linking"; -import * as WebBrowser from "expo-web-browser"; -import { getPostHogApiClient } from "@/lib/posthogApiClient"; import type { InstallCustomMcpServerOptions, InstallMcpTemplateOptions, McpInstallResponse, McpServerInstallation, -} from "./types"; -import { isOAuthRedirect } from "./types"; +} from "@posthog/api-client/types"; +import { isMcpOAuthRedirect } from "@posthog/core/mcp-servers/presentation"; +import * as Linking from "expo-linking"; +import * as WebBrowser from "expo-web-browser"; +import { getPostHogApiClient } from "@/lib/posthogApiClient"; /** Custom URL scheme registered via app.json (`scheme: "posthog"`). The cloud * bounces the OAuth redirect back to this URL once the provider completes @@ -54,7 +54,7 @@ export async function installTemplateWithOAuth( posthog_code_callback_url: OAUTH_CALLBACK_URL, }); - if (!isOAuthRedirect(response)) return response; + if (!isMcpOAuthRedirect(response)) return response; const outcome = await waitForOAuthCallback(response.redirect_url); if (outcome === "cancelled") return "cancelled"; @@ -79,7 +79,7 @@ export async function installCustomWithOAuth( posthog_code_callback_url: OAUTH_CALLBACK_URL, }); - if (!isOAuthRedirect(response)) return response; + if (!isMcpOAuthRedirect(response)) return response; const outcome = await waitForOAuthCallback(response.redirect_url); return outcome === "cancelled" ? "cancelled" : "cancelled"; } diff --git a/apps/mobile/src/features/mcp/sandbox/useMcpUiResource.ts b/apps/mobile/src/features/mcp/sandbox/useMcpUiResource.ts index ad10e21724..5dfb7f8117 100644 --- a/apps/mobile/src/features/mcp/sandbox/useMcpUiResource.ts +++ b/apps/mobile/src/features/mcp/sandbox/useMcpUiResource.ts @@ -4,9 +4,10 @@ import { RESOURCE_MIME_TYPE, } from "@modelcontextprotocol/ext-apps/app-bridge"; import type { Tool } from "@modelcontextprotocol/sdk/types.js"; +import type { McpServerInstallation } from "@posthog/api-client/types"; import { useQuery } from "@tanstack/react-query"; +import type { McpUiResource } from "../mcpUiResource"; import { getMcpConnectionManager } from "../service"; -import type { McpServerInstallation, McpUiResource } from "../types"; interface UseMcpUiResourceArgs { installation: McpServerInstallation | null; diff --git a/apps/mobile/src/features/mcp/types.ts b/apps/mobile/src/features/mcp/types.ts deleted file mode 100644 index c54624db6a..0000000000 --- a/apps/mobile/src/features/mcp/types.ts +++ /dev/null @@ -1,120 +0,0 @@ -// Shared types for MCP server installations and marketplace templates. -// Mirrors the PostHog cloud REST schema (see `apps/code/src/renderer/api/generated.ts`). - -import type { McpUiResourceCsp } from "@modelcontextprotocol/ext-apps/app-bridge"; - -export type McpAuthType = "api_key" | "oauth" | "none"; - -export type McpApprovalState = "approved" | "needs_approval" | "do_not_use"; - -export type McpInstallSource = "posthog" | "posthog-code" | "posthog-mobile"; - -/** Server-side marketplace template — one entry per recommended server. */ -export interface McpRecommendedServer { - id: string; - name: string; - url: string; - docs_url?: string; - description?: string; - auth_type?: McpAuthType; - /** The vendor's brand domain (e.g. "linear.app"), rendered via the - * logo.dev icon proxy. Empty when no brand icon is known. */ - icon_domain?: string; - category?: string; - /** Some templates expose a `transport_type` ("stdio" | "streamable_http"); when - * absent, treat as HTTP. Stdio servers can't run on mobile; we badge them. */ - transport_type?: "stdio" | "streamable_http"; -} - -/** Server-side record of one user's installation of a server. */ -export interface McpServerInstallation { - id: string; - template_id: string | null; - name: string; - /** Brand domain from the linked template, rendered via the logo.dev icon - * proxy. Empty if custom install (no template). */ - icon_domain?: string; - display_name?: string; - url?: string; - description?: string; - auth_type?: McpAuthType; - is_enabled?: boolean; - needs_reauth: boolean; - pending_oauth: boolean; - /** Cloud-hosted proxy URL the client should hit to talk to the MCP server. - * Desktop substitutes a local loopback; mobile uses whatever the API returns. */ - proxy_url: string; - tool_count: number; - transport_type?: "stdio" | "streamable_http"; - created_at: string; - updated_at: string | null; -} - -export interface McpInstallationTool { - id: string; - tool_name: string; - display_name: string; - description: string; - input_schema: unknown; - approval_state?: McpApprovalState; - last_seen_at: string; - removed_at: string | null; - created_at: string; - updated_at: string | null; -} - -export interface McpOAuthRedirectResponse { - redirect_url: string; -} - -export type McpInstallResponse = - | McpServerInstallation - | McpOAuthRedirectResponse; - -export function isOAuthRedirect( - response: McpInstallResponse, -): response is McpOAuthRedirectResponse { - return ( - typeof (response as McpOAuthRedirectResponse).redirect_url === "string" - ); -} - -export interface InstallCustomMcpServerOptions { - name: string; - url: string; - auth_type: McpAuthType; - api_key?: string; - description?: string; - client_id?: string; - client_secret?: string; - install_source?: McpInstallSource; - posthog_code_callback_url?: string; -} - -export interface InstallMcpTemplateOptions { - template_id: string; - api_key?: string; - install_source?: McpInstallSource; - posthog_code_callback_url?: string; -} - -export interface UpdateMcpServerInstallationOptions { - display_name?: string; - description?: string; - is_enabled?: boolean; -} - -export interface McpUiResource { - uri: string; - html: string; - csp?: McpUiResourceCsp; - permissions?: Record>; -} - -/** Returns true if the template/installation requires stdio transport, which - * the mobile app can't host. UI uses this to render a "Desktop only" badge. */ -export function isStdioServer( - s: Pick, -): boolean { - return s.transport_type === "stdio"; -} diff --git a/apps/mobile/src/features/mcp/utils/mcpToolName.test.ts b/apps/mobile/src/features/mcp/utils/mcpToolName.test.ts deleted file mode 100644 index f9982e8658..0000000000 --- a/apps/mobile/src/features/mcp/utils/mcpToolName.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { isMcpToolName, parseMcpToolName } from "./mcpToolName"; - -describe("isMcpToolName", () => { - it("accepts a well-formed MCP tool name", () => { - expect(isMcpToolName("mcp__github__create_issue")).toBe(true); - }); - - it("accepts tool names with extra underscores in the tool segment", () => { - expect(isMcpToolName("mcp__github__list_pull_requests")).toBe(true); - }); - - it("rejects non-MCP tool names", () => { - expect(isMcpToolName("read_file")).toBe(false); - expect(isMcpToolName("Bash")).toBe(false); - expect(isMcpToolName("")).toBe(false); - expect(isMcpToolName(null)).toBe(false); - expect(isMcpToolName(undefined)).toBe(false); - }); - - it("rejects malformed prefixes", () => { - expect(isMcpToolName("mcp_github__tool")).toBe(false); - expect(isMcpToolName("mcp__github")).toBe(false); // no second separator - expect(isMcpToolName("mcp__")).toBe(false); - }); -}); - -describe("parseMcpToolName", () => { - it("splits server and tool", () => { - expect(parseMcpToolName("mcp__linear__create_issue")).toEqual({ - serverName: "linear", - toolName: "create_issue", - }); - }); - - it("keeps double-underscore tool names intact on the tool side", () => { - expect(parseMcpToolName("mcp__db__select__count")).toEqual({ - serverName: "db", - toolName: "select__count", - }); - }); - - it("returns null for invalid names", () => { - expect(parseMcpToolName("read_file")).toBeNull(); - expect(parseMcpToolName("mcp__only-server")).toBeNull(); - expect(parseMcpToolName(null)).toBeNull(); - }); -}); diff --git a/apps/mobile/src/features/mcp/utils/mcpToolName.ts b/apps/mobile/src/features/mcp/utils/mcpToolName.ts deleted file mode 100644 index f2e0273ebb..0000000000 --- a/apps/mobile/src/features/mcp/utils/mcpToolName.ts +++ /dev/null @@ -1,35 +0,0 @@ -// Helpers for detecting + parsing MCP tool names that arrive from the agent. -// -// Cloud agents prefix MCP tool calls with `mcp____` in the raw -// tool name (mobile sees this on `_meta.claudeCode.toolName`). PostHog's own -// MCP plugin already has its own dedicated renderer (`isPostHogExecTool`); we -// pick up everything else. - -const MCP_PREFIX = "mcp__"; - -/** Returns true for any tool name following the MCP naming convention. */ -export function isMcpToolName(toolName: string | undefined | null): boolean { - if (!toolName) return false; - if (!toolName.startsWith(MCP_PREFIX)) return false; - const rest = toolName.slice(MCP_PREFIX.length); - return rest.includes("__"); -} - -export interface ParsedMcpToolName { - serverName: string; - toolName: string; -} - -/** Split `mcp____` into its parts, or `null` if it doesn't match. */ -export function parseMcpToolName( - raw: string | undefined | null, -): ParsedMcpToolName | null { - if (!raw || !raw.startsWith(MCP_PREFIX)) return null; - const rest = raw.slice(MCP_PREFIX.length); - const splitIdx = rest.indexOf("__"); - if (splitIdx <= 0) return null; - return { - serverName: rest.slice(0, splitIdx), - toolName: rest.slice(splitIdx + 2), - }; -} diff --git a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx index b560293b6b..dc7d026439 100644 --- a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx @@ -1,9 +1,9 @@ import { Text } from "@components/text"; import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; +import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import type { TaskRun } from "@posthog/shared"; import { ActivityIndicator, Pressable, View } from "react-native"; -import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; interface AutomationDetailProps { diff --git a/apps/mobile/src/features/tasks/components/AutomationItem.tsx b/apps/mobile/src/features/tasks/components/AutomationItem.tsx index 89d4f4d5de..220f58fdeb 100644 --- a/apps/mobile/src/features/tasks/components/AutomationItem.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationItem.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; +import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import type { TaskRun } from "@posthog/shared"; import { format, formatDistanceToNow } from "date-fns"; import { memo } from "react"; import { Pressable, View } from "react-native"; -import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; interface AutomationItemProps { diff --git a/apps/mobile/src/features/tasks/components/AutomationSkillCard.tsx b/apps/mobile/src/features/tasks/components/AutomationSkillCard.tsx index dace344923..12d7eba64f 100644 --- a/apps/mobile/src/features/tasks/components/AutomationSkillCard.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationSkillCard.tsx @@ -1,3 +1,4 @@ +import type { LlmSkillListItem } from "@posthog/api-client"; import { CaretDown, CaretUp } from "phosphor-react-native"; import { useState } from "react"; import { @@ -8,10 +9,9 @@ import { } from "react-native"; import { Text } from "@/components/text"; import { useThemeColors } from "@/lib/theme"; -import type { SkillStoreListEntry } from "../skills/types"; interface AutomationSkillCardProps { - skill: SkillStoreListEntry; + skill: LlmSkillListItem; onPress: (skillName: string) => void; } diff --git a/apps/mobile/src/features/tasks/components/TaskSessionView.test.tsx b/apps/mobile/src/features/tasks/components/TaskSessionView.test.tsx index 5bab080e15..e55c5f0f72 100644 --- a/apps/mobile/src/features/tasks/components/TaskSessionView.test.tsx +++ b/apps/mobile/src/features/tasks/components/TaskSessionView.test.tsx @@ -24,8 +24,8 @@ vi.mock("@/features/chat", () => ({ deriveToolKind: () => "other", })); -vi.mock("@/features/chat/utils/thinkingMessages", () => ({ - getRandomThinkingActivity: () => "Thinking", +vi.mock("@posthog/core/sessions/thinkingActivities", () => ({ + pickThinkingActivity: () => "Thinking", })); vi.mock("@/lib/theme", () => ({ diff --git a/apps/mobile/src/features/tasks/components/TaskSessionView.tsx b/apps/mobile/src/features/tasks/components/TaskSessionView.tsx index f30b4aec66..75a06938b0 100644 --- a/apps/mobile/src/features/tasks/components/TaskSessionView.tsx +++ b/apps/mobile/src/features/tasks/components/TaskSessionView.tsx @@ -1,3 +1,4 @@ +import { pickThinkingActivity } from "@posthog/core/sessions/thinkingActivities"; import { ArrowDown, Brain, @@ -20,7 +21,6 @@ import { ToolMessage, type ToolStatus, } from "@/features/chat"; -import { getRandomThinkingActivity } from "@/features/chat/utils/thinkingMessages"; import { useThemeColors } from "@/lib/theme"; import type { CloudPendingPermissionRequest, @@ -734,7 +734,9 @@ function useElapsedTimer() { function ThinkingIndicator() { const [dots, setDots] = useState(1); - const [activity, setActivity] = useState(getRandomThinkingActivity); + const [activity, setActivity] = useState(() => + pickThinkingActivity(Math.random()), + ); const elapsed = useElapsedTimer(); const themeColors = useThemeColors(); @@ -747,7 +749,7 @@ function ThinkingIndicator() { useEffect(() => { const interval = setInterval(() => { - setActivity(getRandomThinkingActivity()); + setActivity(pickThinkingActivity(Math.random())); }, 2000); return () => clearInterval(interval); }, []); diff --git a/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts b/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts deleted file mode 100644 index 51231a393c..0000000000 --- a/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const SKILL_TEMPLATE_ID_PREFIX = "llm-skill:"; - -export function formatSkillTemplateId(skillName: string): string { - return `${SKILL_TEMPLATE_ID_PREFIX}${skillName.trim()}`; -} - -export function parseSkillTemplateId( - templateId: string | null | undefined, -): string | null { - if (!templateId?.startsWith(SKILL_TEMPLATE_ID_PREFIX)) { - return null; - } - - const skillName = templateId.slice(SKILL_TEMPLATE_ID_PREFIX.length).trim(); - return skillName || null; -} diff --git a/apps/mobile/src/features/tasks/skills/types.ts b/apps/mobile/src/features/tasks/skills/types.ts deleted file mode 100644 index 7db9836bb0..0000000000 --- a/apps/mobile/src/features/tasks/skills/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SkillStoreListEntry { - name: string; - description: string | null; -} - -export interface SkillStoreSkill extends SkillStoreListEntry { - body: string; -} diff --git a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts index f3e44f2f49..2020007f00 100644 --- a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts +++ b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts @@ -1,5 +1,5 @@ +import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import { describe, expect, it } from "vitest"; -import { getAutomationTemplatePresentation } from "./automationTemplatePresentation"; describe("automationTemplatePresentation", () => { it("prefers repository context when one exists for skill-backed automations", () => { diff --git a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts deleted file mode 100644 index 2f42d89acc..0000000000 --- a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { TaskAutomation } from "@posthog/api-client/posthog-client"; -import { parseSkillTemplateId } from "../skills/skillTemplateIds"; - -export interface AutomationTemplatePresentation { - templateName: string | null; - repositoryLabel: string | null; - contextLabel: string | null; - secondaryLabel: string; -} - -export function getAutomationTemplatePresentation( - automation: Pick, -): AutomationTemplatePresentation { - const repositoryLabel = automation.repository.trim() || null; - const skillName = parseSkillTemplateId(automation.template_id); - const contextLabel = skillName ? "Skill store" : null; - - return { - templateName: - skillName ?? (automation.template_id ? "Template automation" : null), - repositoryLabel, - contextLabel, - secondaryLabel: repositoryLabel ?? contextLabel ?? "No repository context", - }; -} From 57df2f09678b888c2b4efd307ac82c6040115bec Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 01:51:20 +0300 Subject: [PATCH 30/42] test(mobile): use shared inbox helpers Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/features/inbox/utils.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/mobile/src/features/inbox/utils.test.ts b/apps/mobile/src/features/inbox/utils.test.ts index 050f29041f..4b5517264b 100644 --- a/apps/mobile/src/features/inbox/utils.test.ts +++ b/apps/mobile/src/features/inbox/utils.test.ts @@ -1,9 +1,11 @@ +import { buildInboxViewedProperties } from "@posthog/core/inbox/engagement"; import { buildArchiveListOrdering, buildPriorityFilterParam, buildSignalReportListOrdering, INBOX_PIPELINE_STATUSES, } from "@posthog/core/inbox/reportFiltering"; +import { isRestorableReport } from "@posthog/core/inbox/reportMembership"; import { formatSignalReportSummaryMarkdown } from "@posthog/core/inbox/reportPresentation"; import { dismissalReasonLabel } from "@posthog/shared"; import type { @@ -13,11 +15,7 @@ import type { SignalReportStatus, } from "@posthog/shared/domain-types"; import { describe, expect, it } from "vitest"; -import { - buildInboxViewedProperties, - isRestorableReport, - sourceLine, -} from "./utils"; +import { sourceLine } from "./utils"; function signal(source_product: string, source_type: string): Signal { return { From 807299ff2afcbbc05bc4aa8e18810389c91e0c12 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:37:07 +0300 Subject: [PATCH 31/42] refactor(mobile): finalize shared portability boundary Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/(tabs)/inbox.tsx | 1 + apps/mobile/src/app/automation/[id].tsx | 2 +- apps/mobile/src/app/automation/create.tsx | 2 +- .../src/app/mcp-servers/installation/[id].tsx | 2 +- .../src/app/mcp-servers/template/[id].tsx | 2 +- .../src/features/inbox/activityLog.test.ts | 8 +- apps/mobile/src/features/inbox/activityLog.ts | 44 +++++++ .../inbox/components/ArtefactCommit.tsx | 2 +- .../features/inbox/components/DiffBlock.tsx | 2 +- .../inbox/components/ReportActivity.tsx | 7 +- .../features/inbox/components/TinderView.tsx | 4 +- apps/mobile/src/features/inbox/utils.test.ts | 2 +- .../features/mcp/components/McpServerRow.tsx | 2 +- apps/mobile/src/features/mcp/oauth.ts | 2 +- .../mobile/src/features/mcp}/presentation.ts | 0 .../tasks/components/AutomationDetail.tsx | 2 +- .../tasks/components/AutomationItem.tsx | 2 +- .../tasks/composer/TaskChatComposer.tsx | 4 +- .../features/tasks/composer/options.test.ts | 1 + .../src/features/tasks/composer/options.ts | 4 +- .../hooks/useCloudTaskConfigOptions.test.ts | 40 ++++++ .../tasks/hooks/useCloudTaskConfigOptions.ts | 22 +++- .../features/tasks/skills/skillTemplateIds.ts | 13 ++ apps/mobile/src/features/tasks/types.ts | 2 + .../automationTemplatePresentation.test.ts | 2 +- .../utils}/automationTemplatePresentation.ts | 19 +-- apps/mobile/src/lib/posthogApiClient.test.ts | 18 ++- apps/mobile/src/lib/posthogApiClient.ts | 12 +- .../src/automations/automationStatus.test.ts | 80 ----------- .../core/src/automations/automationStatus.ts | 83 ------------ .../core/src/cloud-task/cloud-task-engine.ts | 33 ++++- .../core/src/cloud-task/cloud-task-types.ts | 69 ---------- .../core/src/cloud-task/cloud-task.test.ts | 98 ++++++++++++++ packages/core/src/inbox/activityLog.ts | 45 ------- packages/core/src/inbox/engagement.test.ts | 1 + packages/core/src/inbox/engagement.ts | 57 +++++--- packages/core/src/inbox/reportMembership.ts | 11 -- .../src/integrations/repositories.test.ts | 66 ---------- .../core/src/integrations/repositories.ts | 124 ------------------ packages/core/src/sessions/sessionActivity.ts | 24 +++- .../src/task-detail/composerControls.test.ts | 27 ---- .../core/src/task-detail/composerControls.ts | 99 -------------- .../core/src/tasks/pendingPrompts.test.ts | 4 +- packages/core/src/tasks/pendingPrompts.ts | 6 - packages/core/src/tasks/taskActivity.ts | 20 ++- packages/core/src/tasks/taskArchive.test.ts | 4 + packages/core/src/tasks/taskArchive.ts | 6 +- .../src/tasks/taskStatusPresentation.test.ts | 69 ---------- .../core/src/tasks/taskStatusPresentation.ts | 37 ------ packages/shared/src/index.ts | 12 -- packages/shared/src/task-automation.test.ts | 66 ---------- packages/shared/src/task-automation.ts | 58 -------- .../inbox/hooks/useTrackInboxViewed.ts | 1 + .../components/UnifiedModelSelector.tsx | 5 +- 54 files changed, 393 insertions(+), 935 deletions(-) create mode 100644 apps/mobile/src/features/inbox/activityLog.ts rename {packages/core/src/mcp-servers => apps/mobile/src/features/mcp}/presentation.ts (100%) create mode 100644 apps/mobile/src/features/tasks/skills/skillTemplateIds.ts rename {packages/core/src/automations => apps/mobile/src/features/tasks/utils}/automationTemplatePresentation.ts (64%) delete mode 100644 packages/core/src/automations/automationStatus.test.ts delete mode 100644 packages/core/src/automations/automationStatus.ts delete mode 100644 packages/core/src/cloud-task/cloud-task-types.ts delete mode 100644 packages/core/src/task-detail/composerControls.test.ts delete mode 100644 packages/core/src/task-detail/composerControls.ts delete mode 100644 packages/core/src/tasks/taskStatusPresentation.test.ts delete mode 100644 packages/core/src/tasks/taskStatusPresentation.ts delete mode 100644 packages/shared/src/task-automation.test.ts delete mode 100644 packages/shared/src/task-automation.ts diff --git a/apps/mobile/src/app/(tabs)/inbox.tsx b/apps/mobile/src/app/(tabs)/inbox.tsx index dd66e06c87..bf587fbbe3 100644 --- a/apps/mobile/src/app/(tabs)/inbox.tsx +++ b/apps/mobile/src/app/(tabs)/inbox.tsx @@ -71,6 +71,7 @@ export default function InboxScreen() { visibleReports: reports, totalCount, filters: { + surface: "mobile", sourceProductFilter, statusFilter, suggestedReviewerFilter, diff --git a/apps/mobile/src/app/automation/[id].tsx b/apps/mobile/src/app/automation/[id].tsx index 159742297c..55f9bd5c6d 100644 --- a/apps/mobile/src/app/automation/[id].tsx +++ b/apps/mobile/src/app/automation/[id].tsx @@ -1,6 +1,5 @@ import { Text } from "@components/text"; import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; -import { parseSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useState } from "react"; import { @@ -21,6 +20,7 @@ import { useUpdateTaskAutomation, } from "@/features/tasks/hooks/useAutomations"; import { useTask } from "@/features/tasks/hooks/useTasks"; +import { parseSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds"; import { useThemeColors } from "@/lib/theme"; export default function AutomationDetailScreen() { diff --git a/apps/mobile/src/app/automation/create.tsx b/apps/mobile/src/app/automation/create.tsx index 1fe2c452c0..1f4d662bff 100644 --- a/apps/mobile/src/app/automation/create.tsx +++ b/apps/mobile/src/app/automation/create.tsx @@ -1,5 +1,4 @@ import { TaskAutomationValidationError } from "@posthog/api-client/posthog-client"; -import { formatSkillTemplateId } from "@posthog/core/automations/automationTemplatePresentation"; import { getCalendars } from "expo-localization"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useMemo, useRef, useState } from "react"; @@ -15,6 +14,7 @@ import { Text } from "@/components/text"; import { AutomationForm } from "@/features/tasks/components/AutomationForm"; import { useCreateTaskAutomation } from "@/features/tasks/hooks/useAutomations"; import { useSkillStoreSkill } from "@/features/tasks/skills/hooks"; +import { formatSkillTemplateId } from "@/features/tasks/skills/skillTemplateIds"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { useThemeColors } from "@/lib/theme"; diff --git a/apps/mobile/src/app/mcp-servers/installation/[id].tsx b/apps/mobile/src/app/mcp-servers/installation/[id].tsx index 837edb91c9..2d182b0013 100644 --- a/apps/mobile/src/app/mcp-servers/installation/[id].tsx +++ b/apps/mobile/src/app/mcp-servers/installation/[id].tsx @@ -1,6 +1,5 @@ import { Text } from "@components/text"; import type { McpApprovalState } from "@posthog/api-client/types"; -import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { router, useLocalSearchParams } from "expo-router"; import { ArrowsClockwise, @@ -29,6 +28,7 @@ import { useUpdateMcpToolApproval, } from "@/features/mcp/hooks"; import { reauthorizeInstallation } from "@/features/mcp/oauth"; +import { isStdioMcpServer } from "@/features/mcp/presentation"; import { getMcpConnectionManager } from "@/features/mcp/service"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; diff --git a/apps/mobile/src/app/mcp-servers/template/[id].tsx b/apps/mobile/src/app/mcp-servers/template/[id].tsx index 348c7a488b..50951b052a 100644 --- a/apps/mobile/src/app/mcp-servers/template/[id].tsx +++ b/apps/mobile/src/app/mcp-servers/template/[id].tsx @@ -1,5 +1,4 @@ import { Text } from "@components/text"; -import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { router, useLocalSearchParams } from "expo-router"; import { Lock, Warning } from "phosphor-react-native"; import { useMemo, useState } from "react"; @@ -18,6 +17,7 @@ import { useMcpMarketplace, } from "@/features/mcp/hooks"; import { installTemplateWithOAuth } from "@/features/mcp/oauth"; +import { isStdioMcpServer } from "@/features/mcp/presentation"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { logger } from "@/lib/logger"; import { openExternalUrl } from "@/lib/openExternalUrl"; diff --git a/apps/mobile/src/features/inbox/activityLog.test.ts b/apps/mobile/src/features/inbox/activityLog.test.ts index 1032197c50..4a6d16ce46 100644 --- a/apps/mobile/src/features/inbox/activityLog.test.ts +++ b/apps/mobile/src/features/inbox/activityLog.test.ts @@ -1,12 +1,14 @@ import { attributionLabel, - parseDiffLines, - selectActivityArtefacts, - shortSha, taskRunLabel, } from "@posthog/core/inbox/activityLog"; import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; import { describe, expect, it } from "vitest"; +import { + parseDiffLines, + selectActivityArtefacts, + shortSha, +} from "./activityLog"; function commit(id: string, createdAt: string): AnySignalReportArtefact { return { diff --git a/apps/mobile/src/features/inbox/activityLog.ts b/apps/mobile/src/features/inbox/activityLog.ts new file mode 100644 index 0000000000..7647862c98 --- /dev/null +++ b/apps/mobile/src/features/inbox/activityLog.ts @@ -0,0 +1,44 @@ +import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; + +export type ActivityArtefact = Extract< + AnySignalReportArtefact, + { type: "commit" | "task_run" } +>; + +export function selectActivityArtefacts( + artefacts: AnySignalReportArtefact[], +): ActivityArtefact[] { + return artefacts + .filter( + (artefact): artefact is ActivityArtefact => + artefact.type === "commit" || artefact.type === "task_run", + ) + .sort((left, right) => left.created_at.localeCompare(right.created_at)); +} + +export function shortSha(sha: string): string { + return sha.slice(0, 12); +} + +export type DiffLineKind = "add" | "del" | "hunk" | "context"; + +export interface DiffLine { + text: string; + kind: DiffLineKind; +} + +export function parseDiffLines(diff: string): DiffLine[] { + return diff + .replace(/\n$/, "") + .split("\n") + .map((text) => { + if (text.startsWith("+") && !text.startsWith("+++")) { + return { text, kind: "add" as const }; + } + if (text.startsWith("-") && !text.startsWith("---")) { + return { text, kind: "del" as const }; + } + if (text.startsWith("@@")) return { text, kind: "hunk" as const }; + return { text, kind: "context" as const }; + }); +} diff --git a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx index 3da3aa2355..19f4cc2510 100644 --- a/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx +++ b/apps/mobile/src/features/inbox/components/ArtefactCommit.tsx @@ -1,10 +1,10 @@ import { Text } from "@components/text"; -import { shortSha } from "@posthog/core/inbox/activityLog"; import type { CommitContent } from "@posthog/shared/domain-types"; import { CaretDown, CaretRight } from "phosphor-react-native"; import { useState } from "react"; import { ActivityIndicator, Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; +import { shortSha } from "../activityLog"; import { useCommitDiff } from "../hooks/useInboxReports"; import { DiffBlock } from "./DiffBlock"; diff --git a/apps/mobile/src/features/inbox/components/DiffBlock.tsx b/apps/mobile/src/features/inbox/components/DiffBlock.tsx index f6329cbd3b..4bf6bbc8c5 100644 --- a/apps/mobile/src/features/inbox/components/DiffBlock.tsx +++ b/apps/mobile/src/features/inbox/components/DiffBlock.tsx @@ -1,6 +1,6 @@ import { Text } from "@components/text"; -import { parseDiffLines } from "@posthog/core/inbox/activityLog"; import { ScrollView, View } from "react-native"; +import { parseDiffLines } from "../activityLog"; const LINE_CLASS: Record = { add: "bg-status-success/15 text-status-success", diff --git a/apps/mobile/src/features/inbox/components/ReportActivity.tsx b/apps/mobile/src/features/inbox/components/ReportActivity.tsx index d33b501b2f..27d40725e2 100644 --- a/apps/mobile/src/features/inbox/components/ReportActivity.tsx +++ b/apps/mobile/src/features/inbox/components/ReportActivity.tsx @@ -1,15 +1,12 @@ import { Text } from "@components/text"; -import { - type ActivityArtefact, - attributionLabel, - selectActivityArtefacts, -} from "@posthog/core/inbox/activityLog"; +import { attributionLabel } from "@posthog/core/inbox/activityLog"; import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; import { ClockCounterClockwise } from "phosphor-react-native"; import { useMemo } from "react"; import { View } from "react-native"; import { formatRelativeTime } from "@/lib/format"; import { useThemeColors } from "@/lib/theme"; +import { type ActivityArtefact, selectActivityArtefacts } from "../activityLog"; import { ArtefactCommit } from "./ArtefactCommit"; import { ArtefactTaskRun } from "./ArtefactTaskRun"; diff --git a/apps/mobile/src/features/inbox/components/TinderView.tsx b/apps/mobile/src/features/inbox/components/TinderView.tsx index 373019ff74..2703dfc014 100644 --- a/apps/mobile/src/features/inbox/components/TinderView.tsx +++ b/apps/mobile/src/features/inbox/components/TinderView.tsx @@ -141,7 +141,7 @@ export function TinderView({ const themeColors = useThemeColors(); const router = useRouter(); const insets = useSafeAreaInsets(); - const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); + const { configOptions, isConfigReady } = useCloudTaskConfigOptions("claude"); const model = getModelConfigOption(configOptions).currentValue; // Store state @@ -496,7 +496,7 @@ export function TinderView({ setExpandedReport(null); }} className="h-16 w-16 items-center justify-center rounded-full border-2 border-status-success bg-status-success/10 active:bg-status-success/20" - disabled={creating || !hasLiveConfig} + disabled={creating || !isConfigReady} hitSlop={8} > {creating ? ( diff --git a/apps/mobile/src/features/inbox/utils.test.ts b/apps/mobile/src/features/inbox/utils.test.ts index 4b5517264b..bbdcb62fa8 100644 --- a/apps/mobile/src/features/inbox/utils.test.ts +++ b/apps/mobile/src/features/inbox/utils.test.ts @@ -44,7 +44,7 @@ function buildMobileInboxViewedProperties( return buildInboxViewedProperties({ visibleReports: reports, totalCount, - filters, + filters: { surface: "mobile", ...filters }, }); } diff --git a/apps/mobile/src/features/mcp/components/McpServerRow.tsx b/apps/mobile/src/features/mcp/components/McpServerRow.tsx index 7b0a4b9359..eaa165ea47 100644 --- a/apps/mobile/src/features/mcp/components/McpServerRow.tsx +++ b/apps/mobile/src/features/mcp/components/McpServerRow.tsx @@ -3,11 +3,11 @@ import type { McpRecommendedServer, McpServerInstallation, } from "@posthog/api-client/types"; -import { isStdioMcpServer } from "@posthog/core/mcp-servers/presentation"; import { CaretRight, Lock, Warning } from "phosphor-react-native"; import type { ReactNode } from "react"; import { Pressable, View } from "react-native"; import { useThemeColors } from "@/lib/theme"; +import { isStdioMcpServer } from "../presentation"; import { ServerIcon } from "./ServerIcon"; interface McpServerRowProps { diff --git a/apps/mobile/src/features/mcp/oauth.ts b/apps/mobile/src/features/mcp/oauth.ts index e687baa113..d3648678af 100644 --- a/apps/mobile/src/features/mcp/oauth.ts +++ b/apps/mobile/src/features/mcp/oauth.ts @@ -4,10 +4,10 @@ import type { McpInstallResponse, McpServerInstallation, } from "@posthog/api-client/types"; -import { isMcpOAuthRedirect } from "@posthog/core/mcp-servers/presentation"; import * as Linking from "expo-linking"; import * as WebBrowser from "expo-web-browser"; import { getPostHogApiClient } from "@/lib/posthogApiClient"; +import { isMcpOAuthRedirect } from "./presentation"; /** Custom URL scheme registered via app.json (`scheme: "posthog"`). The cloud * bounces the OAuth redirect back to this URL once the provider completes diff --git a/packages/core/src/mcp-servers/presentation.ts b/apps/mobile/src/features/mcp/presentation.ts similarity index 100% rename from packages/core/src/mcp-servers/presentation.ts rename to apps/mobile/src/features/mcp/presentation.ts diff --git a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx index dc7d026439..b560293b6b 100644 --- a/apps/mobile/src/features/tasks/components/AutomationDetail.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationDetail.tsx @@ -1,9 +1,9 @@ import { Text } from "@components/text"; import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; -import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import type { TaskRun } from "@posthog/shared"; import { ActivityIndicator, Pressable, View } from "react-native"; +import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; interface AutomationDetailProps { diff --git a/apps/mobile/src/features/tasks/components/AutomationItem.tsx b/apps/mobile/src/features/tasks/components/AutomationItem.tsx index 220f58fdeb..89d4f4d5de 100644 --- a/apps/mobile/src/features/tasks/components/AutomationItem.tsx +++ b/apps/mobile/src/features/tasks/components/AutomationItem.tsx @@ -1,11 +1,11 @@ import { Text } from "@components/text"; import type { TaskAutomation } from "@posthog/api-client/posthog-client"; import { formatAutomationScheduleSummary } from "@posthog/core/automations/automationSchedule"; -import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import type { TaskRun } from "@posthog/shared"; import { format, formatDistanceToNow } from "date-fns"; import { memo } from "react"; import { Pressable, View } from "react-native"; +import { getAutomationTemplatePresentation } from "../utils/automationTemplatePresentation"; import { AutomationStatusBadge } from "./AutomationStatusBadge"; interface AutomationItemProps { diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index e395b7cf00..b308f7ebc8 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -537,7 +537,9 @@ export function TaskChatComposer({ reasoning, }); onModelChange(next.model); - onReasoningChange(next.reasoning); + if (next.reasoning !== reasoning) { + onReasoningChange(next.reasoning); + } }} onClose={() => setModelSheetOpen(false)} options={mobileModelOptions.map((m) => ({ diff --git a/apps/mobile/src/features/tasks/composer/options.test.ts b/apps/mobile/src/features/tasks/composer/options.test.ts index 76675adc5d..567276219c 100644 --- a/apps/mobile/src/features/tasks/composer/options.test.ts +++ b/apps/mobile/src/features/tasks/composer/options.test.ts @@ -67,6 +67,7 @@ describe("mobile composer options", () => { [{ hasContent: true }, "send"], [{ canStop: true }, "stop"], [{ isRecording: true }, "mic-stop"], + [{ isRecording: true, canStop: true }, "mic-stop"], [{}, "mic"], ])("derives the mobile primary action", (overrides, expected) => { expect( diff --git a/apps/mobile/src/features/tasks/composer/options.ts b/apps/mobile/src/features/tasks/composer/options.ts index 35daf8f3e0..85777b78d7 100644 --- a/apps/mobile/src/features/tasks/composer/options.ts +++ b/apps/mobile/src/features/tasks/composer/options.ts @@ -68,7 +68,7 @@ export function resolveComposerPrimaryAction({ allowSendWhileRunning: boolean; }): ComposerPrimaryAction { if (disabled || isTranscribing) return "disabled"; + if (isRecording) return "mic-stop"; if (canStop && (!allowSendWhileRunning || !hasContent)) return "stop"; - if (hasContent && !isRecording) return "send"; - return isRecording ? "mic-stop" : "mic"; + return hasContent ? "send" : "mic"; } diff --git a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts index 4b516f1445..b3eeace005 100644 --- a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts +++ b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.test.ts @@ -106,6 +106,32 @@ describe("useCloudTaskConfigOptions", () => { expect(mockGetCloudTaskConfigOptions).toHaveBeenCalledWith("claude"); }); + it("replaces a hidden GLM current model with a visible model", async () => { + mockGetCloudTaskConfigOptions.mockResolvedValue([ + { + id: "model", + name: "Model", + type: "select", + currentValue: "@cf/zai-org/glm-5.2", + options: [ + { value: "@cf/zai-org/glm-5.2", name: "GLM-5.2" }, + { value: "claude-sonnet-5", name: "Claude Sonnet 5" }, + ], + category: "model", + description: "Choose a model", + }, + ] satisfies CloudTaskConfigOption[]); + + const result = await renderHook(); + await waitForAssertion(() => { + const modelOption = getModelConfigOption(result.current.configOptions); + expect(modelOption.currentValue).toBe("claude-sonnet-5"); + expect(modelOption.options.map((option) => option.value)).toEqual([ + "claude-sonnet-5", + ]); + }); + }); + it("keeps the shared fallback when unauthenticated", async () => { mockUseAuthStore.mockImplementation((selector) => selector({ oauthAccessToken: null }), @@ -117,5 +143,19 @@ describe("useCloudTaskConfigOptions", () => { getModelConfigOption(result.current.configOptions).currentValue, ).toBe(DEFAULT_GATEWAY_MODEL); expect(mockGetCloudTaskConfigOptions).not.toHaveBeenCalled(); + expect(result.current.isConfigReady).toBe(true); + }); + + it("makes the shared fallback usable after the live catalog fails", async () => { + mockGetCloudTaskConfigOptions.mockRejectedValue(new Error("offline")); + + const result = await renderHook(); + await waitForAssertion(() => { + expect(result.current.isConfigReady).toBe(true); + }); + + expect( + getModelConfigOption(result.current.configOptions).currentValue, + ).toBe(DEFAULT_GATEWAY_MODEL); }); }); diff --git a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts index aaedf3375d..728c68842d 100644 --- a/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts +++ b/apps/mobile/src/features/tasks/hooks/useCloudTaskConfigOptions.ts @@ -4,6 +4,7 @@ import { type CloudTaskConfigOption, GLM_MODEL_FLAG, isGlmModelId, + isRestrictedModelOption, } from "@posthog/shared"; import { useQuery } from "@tanstack/react-query"; import { useFeatureFlag } from "posthog-react-native"; @@ -35,12 +36,21 @@ export function useCloudTaskConfigOptions(adapter: Adapter = "claude") { ? configOptions : configOptions.map((option) => option.category === "model" - ? { - ...option, - options: option.options.filter( + ? (() => { + const options = option.options.filter( (model) => !isGlmModelId(model.value), - ), - } + ); + const currentValue = options.some( + (model) => + model.value === option.currentValue && + !isRestrictedModelOption(model._meta), + ) + ? option.currentValue + : (options.find( + (model) => !isRestrictedModelOption(model._meta), + )?.value ?? option.currentValue); + return { ...option, currentValue, options }; + })() : option, ); @@ -48,5 +58,7 @@ export function useCloudTaskConfigOptions(adapter: Adapter = "claude") { ...query, configOptions: visibleConfigOptions, hasLiveConfig: query.data !== undefined, + isConfigReady: + !oauthAccessToken || query.data !== undefined || query.isError, }; } diff --git a/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts b/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts new file mode 100644 index 0000000000..deff420f6c --- /dev/null +++ b/apps/mobile/src/features/tasks/skills/skillTemplateIds.ts @@ -0,0 +1,13 @@ +export const SKILL_TEMPLATE_ID_PREFIX = "llm-skill:"; + +export function formatSkillTemplateId(skillName: string): string { + return `${SKILL_TEMPLATE_ID_PREFIX}${skillName.trim()}`; +} + +export function parseSkillTemplateId( + templateId: string | null | undefined, +): string | null { + if (!templateId?.startsWith(SKILL_TEMPLATE_ID_PREFIX)) return null; + const skillName = templateId.slice(SKILL_TEMPLATE_ID_PREFIX.length).trim(); + return skillName || null; +} diff --git a/apps/mobile/src/features/tasks/types.ts b/apps/mobile/src/features/tasks/types.ts index ad45e83576..7f16dcbd20 100644 --- a/apps/mobile/src/features/tasks/types.ts +++ b/apps/mobile/src/features/tasks/types.ts @@ -3,6 +3,8 @@ import type { CloudTaskPermissionRequestUpdate, } from "@posthog/shared"; +export type TerminalStatus = "completed" | "failed" | "stopped"; + export interface SessionNotificationAttachment { kind: "image" | "document"; uri: string; diff --git a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts index 2020007f00..f3e44f2f49 100644 --- a/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts +++ b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.test.ts @@ -1,5 +1,5 @@ -import { getAutomationTemplatePresentation } from "@posthog/core/automations/automationTemplatePresentation"; import { describe, expect, it } from "vitest"; +import { getAutomationTemplatePresentation } from "./automationTemplatePresentation"; describe("automationTemplatePresentation", () => { it("prefers repository context when one exists for skill-backed automations", () => { diff --git a/packages/core/src/automations/automationTemplatePresentation.ts b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts similarity index 64% rename from packages/core/src/automations/automationTemplatePresentation.ts rename to apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts index 7d73244aa7..f116b68d5e 100644 --- a/packages/core/src/automations/automationTemplatePresentation.ts +++ b/apps/mobile/src/features/tasks/utils/automationTemplatePresentation.ts @@ -1,23 +1,10 @@ import type { TaskAutomation } from "@posthog/api-client/posthog-client"; - -export const SKILL_TEMPLATE_ID_PREFIX = "llm-skill:"; - -export function formatSkillTemplateId(skillName: string): string { - return `${SKILL_TEMPLATE_ID_PREFIX}${skillName.trim()}`; -} - -export function parseSkillTemplateId( - templateId: string | null | undefined, -): string | null { - if (!templateId?.startsWith(SKILL_TEMPLATE_ID_PREFIX)) return null; - const skillName = templateId.slice(SKILL_TEMPLATE_ID_PREFIX.length).trim(); - return skillName || null; -} +import { parseSkillTemplateId } from "../skills/skillTemplateIds"; export interface AutomationTemplatePresentation { templateName: string | null; - repositoryLabel: string | null; contextLabel: string | null; + repositoryLabel: string | null; secondaryLabel: string; } @@ -30,8 +17,8 @@ export function getAutomationTemplatePresentation( return { templateName: skillName ?? (automation.template_id ? "Template automation" : null), - repositoryLabel, contextLabel, + repositoryLabel, secondaryLabel: repositoryLabel ?? contextLabel ?? "No repository context", }; } diff --git a/apps/mobile/src/lib/posthogApiClient.test.ts b/apps/mobile/src/lib/posthogApiClient.test.ts index 2aae2a0ed2..9585278ff2 100644 --- a/apps/mobile/src/lib/posthogApiClient.test.ts +++ b/apps/mobile/src/lib/posthogApiClient.test.ts @@ -97,13 +97,29 @@ describe("createPostHogApiClient", () => { teamId: 123, options: { appVersion: "1.2.3", - fetch: mocks.expoFetch, githubConnectFrom: "posthog_mobile", userAgent: "posthog/mobile.hog.dev; version: 1.2.3", }, }); }); + it("converts URL inputs before calling Expo fetch", async () => { + const { createPostHogApiClient } = await import("./posthogApiClient"); + createPostHogApiClient(); + const mobileFetch = mocks.instances[0]?.options.fetch as typeof fetch; + const init = { method: "GET" }; + + await mobileFetch( + new URL("https://us.posthog.com/api/projects/2/tasks/"), + init, + ); + + expect(mocks.expoFetch).toHaveBeenCalledWith( + "https://us.posthog.com/api/projects/2/tasks/", + init, + ); + }); + it("falls back to the Expo config version", async () => { mocks.expoApplication.nativeApplicationVersion = null; mocks.expoConstants.expoConfig = { version: "4.5.6" }; diff --git a/apps/mobile/src/lib/posthogApiClient.ts b/apps/mobile/src/lib/posthogApiClient.ts index c094452f44..7879cb5bf6 100644 --- a/apps/mobile/src/lib/posthogApiClient.ts +++ b/apps/mobile/src/lib/posthogApiClient.ts @@ -11,6 +11,16 @@ const MOBILE_GITHUB_CONNECT_FROM = "posthog_mobile"; let posthogApiClient: PostHogAPIClient | null = null; let posthogApiHost: string | null = null; +const mobileFetch: FetchImplementation = (input, init) => + fetch( + typeof input === "string" + ? input + : input instanceof URL + ? input.toString() + : input.url, + init, + ); + function getAppVersion(): string { return ( Application.nativeApplicationVersion ?? @@ -63,7 +73,7 @@ export function createPostHogApiClient(): PostHogAPIClient { projectId, { appVersion, - fetch: fetch as FetchImplementation, + fetch: mobileFetch, githubConnectFrom: MOBILE_GITHUB_CONNECT_FROM, userAgent: `posthog/mobile.hog.dev; version: ${appVersion}`, }, diff --git a/packages/core/src/automations/automationStatus.test.ts b/packages/core/src/automations/automationStatus.test.ts deleted file mode 100644 index 862fc3c0b5..0000000000 --- a/packages/core/src/automations/automationStatus.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { - type AutomationStatusPresentation, - type AutomationTaskRunStatus, - getAutomationStatusPresentation, -} from "./automationStatus"; - -describe("automationStatus", () => { - it.each<{ - status: AutomationTaskRunStatus; - expected: AutomationStatusPresentation | null; - }>([ - { - status: "not_started", - expected: { label: "Queued", tone: "warning", iconKind: "queued" }, - }, - { - status: "queued", - expected: { label: "Queued", tone: "warning", iconKind: "queued" }, - }, - { status: "started", expected: null }, - { status: "in_progress", expected: null }, - { - status: "completed", - expected: { label: "Success", tone: "success", iconKind: "success" }, - }, - { - status: "failed", - expected: { label: "Failed", tone: "error", iconKind: "failed" }, - }, - { - status: "cancelled", - expected: { label: "Failed", tone: "error", iconKind: "failed" }, - }, - ])( - "maps task-run status $status to renderer-neutral presentation data", - ({ status, expected }) => { - expect( - getAutomationStatusPresentation({ - lastRunStatus: "success", - lastTaskRunStatus: status, - }), - ).toEqual(expected); - }, - ); - - it.each([ - ["running", null], - ["success", { label: "Success", tone: "success", iconKind: "success" }], - ["failed", { label: "Failed", tone: "error", iconKind: "failed" }], - [null, { label: "Never run", tone: "neutral", iconKind: "never-run" }], - ["unknown", { label: "Never run", tone: "neutral", iconKind: "never-run" }], - ] as const)( - "falls back from automation status %j to semantic presentation data", - (lastRunStatus, expected) => { - expect(getAutomationStatusPresentation({ lastRunStatus })).toEqual( - expected, - ); - }, - ); - - it("prioritizes linked task-run detail over the automation-level status", () => { - expect( - getAutomationStatusPresentation({ - lastRunStatus: "failed", - lastTaskRunStatus: "completed", - }), - ).toEqual({ - label: "Success", - tone: "success", - iconKind: "success", - }); - }); - - it("does not expose renderer-specific class names", () => { - expect( - getAutomationStatusPresentation({ lastRunStatus: "success" }), - ).not.toHaveProperty("className"); - }); -}); diff --git a/packages/core/src/automations/automationStatus.ts b/packages/core/src/automations/automationStatus.ts deleted file mode 100644 index 4bcc13735e..0000000000 --- a/packages/core/src/automations/automationStatus.ts +++ /dev/null @@ -1,83 +0,0 @@ -export type AutomationTaskRunStatus = - | "not_started" - | "queued" - | "started" - | "in_progress" - | "completed" - | "failed" - | "cancelled"; - -export interface AutomationStatusInput { - lastRunStatus: string | null; - lastTaskRunStatus?: AutomationTaskRunStatus | null; -} - -export type AutomationStatusTone = "neutral" | "warning" | "success" | "error"; - -export type AutomationStatusIconKind = - | "queued" - | "success" - | "failed" - | "never-run"; - -export interface AutomationStatusPresentation { - label: string; - tone: AutomationStatusTone; - iconKind: AutomationStatusIconKind; -} - -export function getAutomationStatusPresentation({ - lastRunStatus, - lastTaskRunStatus, -}: AutomationStatusInput): AutomationStatusPresentation | null { - switch (lastTaskRunStatus) { - case "not_started": - case "queued": - return { - label: "Queued", - tone: "warning", - iconKind: "queued", - }; - case "started": - case "in_progress": - return null; - case "completed": - return { - label: "Success", - tone: "success", - iconKind: "success", - }; - case "failed": - case "cancelled": - return { - label: "Failed", - tone: "error", - iconKind: "failed", - }; - default: - break; - } - - switch (lastRunStatus) { - case "running": - return null; - case "success": - return { - label: "Success", - tone: "success", - iconKind: "success", - }; - case "failed": - return { - label: "Failed", - tone: "error", - iconKind: "failed", - }; - default: - return { - label: "Never run", - tone: "neutral", - iconKind: "never-run", - }; - } -} diff --git a/packages/core/src/cloud-task/cloud-task-engine.ts b/packages/core/src/cloud-task/cloud-task-engine.ts index 006b0ab6d2..6207e32e64 100644 --- a/packages/core/src/cloud-task/cloud-task-engine.ts +++ b/packages/core/src/cloud-task/cloud-task-engine.ts @@ -34,6 +34,7 @@ const SSE_HEALTHY_CONNECTION_MS = 60_000; const EVENT_BATCH_FLUSH_MS = 16; const EVENT_BATCH_MAX_SIZE = 50; const SESSION_LOG_PAGE_LIMIT = 5_000; +const ARCHIVED_LOG_FETCH_TIMEOUT_MS = 15_000; const MAX_HANDLED_RELAY_REQUEST_IDS = 1_000; const MCP_RELAY_METHODS_WITHOUT_APPROVAL = new Set([ "initialize", @@ -81,6 +82,7 @@ class BackendStreamError extends Error { interface TaskRunResponse { id: string; + log_url?: string | null; status: TaskRunStatus; stage?: string | null; output?: Record | null; @@ -1106,7 +1108,10 @@ export class CloudTaskEngine extends TypedEventEmitter { } if (isTerminalStatus(run.status)) { - const historicalEntries = await this.fetchAllSessionLogs(watcher); + let historicalEntries = await this.fetchAllSessionLogs(watcher); + if (historicalEntries?.length === 0 && run.log_url) { + historicalEntries = await this.fetchArchivedLogs(run.log_url); + } const terminalWatcher = this.watchers.get(key); if (!terminalWatcher || terminalWatcher !== watcher) return; if (watcher.failed) return; @@ -2154,6 +2159,32 @@ export class CloudTaskEngine extends TypedEventEmitter { } } + private async fetchArchivedLogs( + logUrl: string, + ): Promise { + try { + const response = await this.streamFetch(logUrl, { + signal: AbortSignal.timeout(ARCHIVED_LOG_FETCH_TIMEOUT_MS), + }); + if (!response.ok) return null; + const content = await response.text(); + if (!content.trim()) return []; + return content + .trim() + .split("\n") + .flatMap((line) => { + try { + return [JSON.parse(line) as StoredLogEntry]; + } catch { + return []; + } + }); + } catch (error) { + this.log.warn("Cloud task archived logs fetch error", { error }); + return null; + } + } + private async resolveStreamTarget(watcher: WatcherState): Promise { const url = `${watcher.apiHost}/api/projects/${watcher.teamId}/tasks/${watcher.taskId}/runs/${watcher.runId}/stream_token/`; try { diff --git a/packages/core/src/cloud-task/cloud-task-types.ts b/packages/core/src/cloud-task/cloud-task-types.ts deleted file mode 100644 index a2cd0c377e..0000000000 --- a/packages/core/src/cloud-task/cloud-task-types.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { StoredLogEntry, TaskRunStatus } from "@posthog/shared"; - -interface CloudTaskUpdateBase { - taskId: string; - runId: string; -} - -export interface CloudTaskLogsUpdate extends CloudTaskUpdateBase { - kind: "logs"; - newEntries: StoredLogEntry[]; - totalEntryCount: number; -} - -export interface CloudTaskStatusUpdate extends CloudTaskUpdateBase { - kind: "status"; - status?: TaskRunStatus; - stage?: string | null; - output?: Record | null; - errorMessage?: string | null; - branch?: string | null; - sandboxAlive?: boolean | null; -} - -export interface CloudTaskSnapshotUpdate extends CloudTaskUpdateBase { - kind: "snapshot"; - newEntries: StoredLogEntry[]; - totalEntryCount: number; - status?: TaskRunStatus; - stage?: string | null; - output?: Record | null; - errorMessage?: string | null; - branch?: string | null; - sandboxAlive?: boolean | null; -} - -export interface CloudTaskErrorUpdate extends CloudTaskUpdateBase { - kind: "error"; - errorTitle: string; - errorMessage: string; - retryable: boolean; -} - -export interface CloudPermissionOption { - kind: string; - optionId: string; - name: string; - _meta?: Record; -} - -export interface CloudTaskPermissionRequestUpdate extends CloudTaskUpdateBase { - kind: "permission_request"; - requestId: string; - toolCall: { - toolCallId: string; - title: string; - kind: string; - content?: unknown[]; - rawInput?: Record; - _meta?: Record; - }; - options: CloudPermissionOption[]; -} - -export type CloudTaskUpdatePayload = - | CloudTaskLogsUpdate - | CloudTaskStatusUpdate - | CloudTaskSnapshotUpdate - | CloudTaskErrorUpdate - | CloudTaskPermissionRequestUpdate; diff --git a/packages/core/src/cloud-task/cloud-task.test.ts b/packages/core/src/cloud-task/cloud-task.test.ts index 4178bd3d78..dcba899d9a 100644 --- a/packages/core/src/cloud-task/cloud-task.test.ts +++ b/packages/core/src/cloud-task/cloud-task.test.ts @@ -2532,6 +2532,104 @@ describe("CloudTaskEngine", () => { expect(statusFetchCount).toBeLessThanOrEqual(2); }); + it("loads archived logs when a terminal run has no persisted session logs", async () => { + const updates: unknown[] = []; + service.on(CloudTaskEvent.Update, (payload) => updates.push(payload)); + const archivedEntry = { + type: "notification", + timestamp: "2026-01-01T00:00:00Z", + }; + + mockNetFetch.mockImplementation((input: string | Request) => { + const url = typeof input === "string" ? input : input.url; + if (url.includes("/session_logs/")) { + return Promise.resolve( + createJsonResponse([], 200, { "X-Has-More": "false" }), + ); + } + if (url === "https://logs.example.com/run-1.jsonl") { + return Promise.resolve( + new Response(`${JSON.stringify(archivedEntry)}\n`, { status: 200 }), + ); + } + return Promise.resolve( + createJsonResponse({ + id: "run-1", + status: "completed", + log_url: "https://logs.example.com/run-1.jsonl", + updated_at: "2026-01-01T00:00:00Z", + }), + ); + }); + + service.watch({ + taskId: "task-1", + runId: "run-1", + apiHost: "https://app.example.com", + teamId: 2, + }); + + await waitFor(() => updates.length === 1); + expect(updates[0]).toEqual( + expect.objectContaining({ + kind: "snapshot", + newEntries: [archivedEntry], + totalEntryCount: 1, + status: "completed", + }), + ); + expect( + mockNetFetch.mock.calls.find( + ([input]) => input === "https://logs.example.com/run-1.jsonl", + )?.[1]?.signal, + ).toBeInstanceOf(AbortSignal); + }); + + it("keeps valid archived entries around malformed lines", async () => { + const updates: unknown[] = []; + service.on(CloudTaskEvent.Update, (payload) => updates.push(payload)); + const archivedEntry = { + type: "notification", + timestamp: "2026-01-01T00:00:00Z", + }; + + mockNetFetch.mockImplementation((input: string | Request) => { + const url = typeof input === "string" ? input : input.url; + if (url.includes("/session_logs/")) { + return Promise.resolve( + createJsonResponse([], 200, { "X-Has-More": "false" }), + ); + } + if (url === "https://logs.example.com/run-1.jsonl") { + return Promise.resolve( + new Response(`invalid\n${JSON.stringify(archivedEntry)}\n`, { + status: 200, + }), + ); + } + return Promise.resolve( + createJsonResponse({ + id: "run-1", + status: "completed", + log_url: "https://logs.example.com/run-1.jsonl", + updated_at: "2026-01-01T00:00:00Z", + }), + ); + }); + + service.watch({ + taskId: "task-1", + runId: "run-1", + apiHost: "https://app.example.com", + teamId: 2, + }); + + await waitFor(() => updates.length === 1); + expect(updates[0]).toEqual( + expect.objectContaining({ newEntries: [archivedEntry] }), + ); + }); + const guardedFetchStatusExpectations = [ [ 401, diff --git a/packages/core/src/inbox/activityLog.ts b/packages/core/src/inbox/activityLog.ts index 11fb75cab2..6f6eeab993 100644 --- a/packages/core/src/inbox/activityLog.ts +++ b/packages/core/src/inbox/activityLog.ts @@ -1,25 +1,3 @@ -import type { AnySignalReportArtefact } from "@posthog/shared/domain-types"; - -export type ActivityArtefact = Extract< - AnySignalReportArtefact, - { type: "commit" | "task_run" } ->; - -export function selectActivityArtefacts( - artefacts: AnySignalReportArtefact[], -): ActivityArtefact[] { - return artefacts - .filter( - (artefact): artefact is ActivityArtefact => - artefact.type === "commit" || artefact.type === "task_run", - ) - .sort((left, right) => left.created_at.localeCompare(right.created_at)); -} - -export function shortSha(sha: string): string { - return sha.slice(0, 12); -} - const SIGNALS_TYPE_LABELS: Record = { research: "Research", implementation: "Implementation", @@ -49,26 +27,3 @@ export function attributionLabel(artefact: { } return artefact.task_id ? "agent" : null; } - -export type DiffLineKind = "add" | "del" | "hunk" | "context"; - -export interface DiffLine { - text: string; - kind: DiffLineKind; -} - -export function parseDiffLines(diff: string): DiffLine[] { - return diff - .replace(/\n$/, "") - .split("\n") - .map((text) => { - if (text.startsWith("+") && !text.startsWith("+++")) { - return { text, kind: "add" as const }; - } - if (text.startsWith("-") && !text.startsWith("---")) { - return { text, kind: "del" as const }; - } - if (text.startsWith("@@")) return { text, kind: "hunk" as const }; - return { text, kind: "context" as const }; - }); -} diff --git a/packages/core/src/inbox/engagement.test.ts b/packages/core/src/inbox/engagement.test.ts index f880ace30c..c405ee2621 100644 --- a/packages/core/src/inbox/engagement.test.ts +++ b/packages/core/src/inbox/engagement.test.ts @@ -28,6 +28,7 @@ function fakeReport(overrides: Partial = {}): SignalReport { } const NO_FILTERS = { + surface: "desktop" as const, sourceProductFilter: [], priorityFilter: [], searchQuery: "", diff --git a/packages/core/src/inbox/engagement.ts b/packages/core/src/inbox/engagement.ts index 012ca18dc4..4cc82b7ea5 100644 --- a/packages/core/src/inbox/engagement.ts +++ b/packages/core/src/inbox/engagement.ts @@ -171,22 +171,36 @@ export function buildBulkActionEvents( })); } -export interface InboxViewedFilterState { +interface InboxViewedFilterStateBase { sourceProductFilter: string[]; priorityFilter: string[]; - searchQuery?: string; - statusFilter?: readonly string[]; - defaultStatusFilter?: readonly string[]; - suggestedReviewerFilter?: string[]; +} + +export interface DesktopInboxViewedFilterState + extends InboxViewedFilterStateBase { + surface: "desktop"; + searchQuery: string; /** * True when the reviewer scope is the default ("For you"). False when the * user has narrowed to a teammate or the whole project — treated as an * active filter for `has_active_filters`. */ - isDefaultScope?: boolean; + isDefaultScope: boolean; +} + +export interface MobileInboxViewedFilterState + extends InboxViewedFilterStateBase { + surface: "mobile"; + statusFilter: readonly string[]; + defaultStatusFilter: readonly string[]; + suggestedReviewerFilter: string[]; } -export interface BuildInboxViewedInput { +export type InboxViewedFilterState = + | DesktopInboxViewedFilterState + | MobileInboxViewedFilterState; + +interface BuildInboxViewedInputBase { /** * Reports currently visible to the user (after reviewer scope + search), used * for `report_count`, `ready_count`, and the priority/actionability breakdown. @@ -194,11 +208,19 @@ export interface BuildInboxViewedInput { visibleReports: SignalReport[]; /** Server-reported total of reports matching the active query — the headline inbox number. */ totalCount: number; - /** Tab badge counts shown in the v2 header (the numbers the user actually sees). */ - tabCounts?: { pulls: number; reports: number }; - filters: InboxViewedFilterState; } +export type BuildInboxViewedInput = + | (BuildInboxViewedInputBase & { + filters: DesktopInboxViewedFilterState; + /** Tab badge counts shown in the desktop header. */ + tabCounts: { pulls: number; reports: number }; + }) + | (BuildInboxViewedInputBase & { + filters: MobileInboxViewedFilterState; + tabCounts?: never; + }); + /** * Build the property payload for the `Inbox viewed` analytics event from the * v2 inbox state. Pure so it can be unit-tested and reused across hosts. @@ -242,19 +264,19 @@ export function buildInboxViewedProperties( } const statusFiltered = - filters.statusFilter !== undefined && - filters.defaultStatusFilter !== undefined && + filters.surface === "mobile" && (filters.statusFilter.length !== filters.defaultStatusFilter.length || filters.statusFilter.some( - (status) => !filters.defaultStatusFilter?.includes(status), + (status) => !filters.defaultStatusFilter.includes(status), )); const hasActiveFilters = filters.sourceProductFilter.length > 0 || filters.priorityFilter.length > 0 || - (filters.searchQuery?.trim().length ?? 0) > 0 || + (filters.surface === "desktop" && filters.searchQuery.trim().length > 0) || statusFiltered || - (filters.suggestedReviewerFilter?.length ?? 0) > 0 || - filters.isDefaultScope === false; + (filters.surface === "mobile" && + filters.suggestedReviewerFilter.length > 0) || + (filters.surface === "desktop" && !filters.isDefaultScope); return { report_count: visibleReports.length, @@ -262,7 +284,8 @@ export function buildInboxViewedProperties( ready_count: readyCount, has_active_filters: hasActiveFilters, source_product_filter: filters.sourceProductFilter, - status_filter_count: filters.statusFilter?.length ?? 0, + status_filter_count: + filters.surface === "mobile" ? filters.statusFilter.length : 0, is_empty: totalCount === 0, priority_p0_count: priorityCounts.P0, priority_p1_count: priorityCounts.P1, diff --git a/packages/core/src/inbox/reportMembership.ts b/packages/core/src/inbox/reportMembership.ts index e31621fba3..926205012c 100644 --- a/packages/core/src/inbox/reportMembership.ts +++ b/packages/core/src/inbox/reportMembership.ts @@ -41,17 +41,6 @@ export function isRestorableReport( return report.status === "suppressed"; } -export function getImmediatelyActionableReports( - reports: SignalReport[], -): SignalReport[] { - return reports.filter( - (report) => - report.status === "ready" && - report.actionability === "immediately_actionable" && - !report.already_addressed, - ); -} - export type InboxScope = "for-you" | "entire-project" | `teammate:${string}`; export const INBOX_SCOPE_FOR_YOU: InboxScope = "for-you"; diff --git a/packages/core/src/integrations/repositories.test.ts b/packages/core/src/integrations/repositories.test.ts index 80eec8ad36..ce6af19540 100644 --- a/packages/core/src/integrations/repositories.test.ts +++ b/packages/core/src/integrations/repositories.test.ts @@ -1,7 +1,5 @@ import { describe, expect, it } from "vitest"; import { - buildTeamRepositoryOptions, - buildUserRepositoryOptions, combineGithubRepositories, combineRepositoryPicker, combineUserGithubRepositories, @@ -9,11 +7,8 @@ import { isEmptyRepositoryMap, isRepoInIntegration, normalizeRepoKey, - normalizeRepositoryNames, type RepositoryCacheAction, type RepositoryQueryResult, - repositoryLoadWarning, - repositoryOptionsEqual, resolveEffectiveUserRepositoryMap, resolveUserRepositoryCacheAction, sameUserRepositoryMap, @@ -23,67 +18,6 @@ import { type UserRepositoryIntegrationRef, } from "./repositories"; -describe("repository options", () => { - it("normalizes repository names", () => { - expect(normalizeRepositoryNames(["PostHog/Code", ""])).toEqual([ - "posthog/code", - ]); - }); - - it("builds sorted team options with integration labels", () => { - expect( - buildTeamRepositoryOptions( - [ - { id: 2, display_name: "Work" }, - { id: 1, config: { account: { login: "personal" } } }, - ], - { 1: ["z/repo"], 2: ["a/repo"] }, - ), - ).toEqual([ - { integrationId: 2, integrationLabel: "Work", repository: "a/repo" }, - { - integrationId: 1, - integrationLabel: "personal", - repository: "z/repo", - }, - ]); - }); - - it("builds user options with the same shape", () => { - expect( - buildUserRepositoryOptions( - [{ id: "user-1", installation_id: "42", account: { name: "Me" } }], - { 42: ["posthog/code"] }, - ), - ).toEqual([ - { integrationId: 42, integrationLabel: "Me", repository: "posthog/code" }, - ]); - }); - - it.each([ - [0, 2, null], - [1, 2, "Some GitHub repositories could not be loaded. Pull to retry."], - [2, 2, "Could not load GitHub repositories. Pull to retry."], - ])( - "describes %i of %i failed repository loads", - (failed, total, expected) => { - expect(repositoryLoadWarning(failed, total)).toBe(expected); - }, - ); - - it("compares option lists by content", () => { - const options = [ - { integrationId: 1, integrationLabel: "Me", repository: "a/repo" }, - ]; - expect( - repositoryOptionsEqual( - options, - options.map((option) => ({ ...option })), - ), - ).toBe(true); - }); -}); - function result( data: T | undefined, flags: Partial, "data">> = {}, diff --git a/packages/core/src/integrations/repositories.ts b/packages/core/src/integrations/repositories.ts index ea9bb6d202..43a7fbf74e 100644 --- a/packages/core/src/integrations/repositories.ts +++ b/packages/core/src/integrations/repositories.ts @@ -5,130 +5,6 @@ export interface RepositoryQueryResult { isRefetching: boolean; } -export interface RepositoryOption { - integrationId: number; - integrationLabel: string; - repository: string; -} - -export interface RepositorySelection { - integrationId: number | null; - repository: string | null; -} - -export interface TeamRepositoryIntegration { - id: number; - display_name?: string; - config?: { account?: { login?: string } }; -} - -export interface UserRepositoryIntegration { - id: string; - installation_id: string; - account?: { name?: string | null } | null; -} - -export function normalizeRepositoryNames( - repositories: ReadonlyArray, -): string[] { - return repositories - .map((repository) => repository.toLowerCase()) - .filter((repository) => repository.length > 0); -} - -export function repositoryLoadWarning( - failedCount: number, - totalCount: number, -): string | null { - if (failedCount === 0) return null; - return failedCount === totalCount - ? "Could not load GitHub repositories. Pull to retry." - : "Some GitHub repositories could not be loaded. Pull to retry."; -} - -export function buildTeamRepositoryOptions( - integrations: ReadonlyArray, - repositoriesByIntegration: Readonly>, -): RepositoryOption[] { - return integrations - .flatMap((integration) => - (repositoriesByIntegration[integration.id] ?? []).map((repository) => ({ - integrationId: integration.id, - integrationLabel: - integration.display_name ?? - integration.config?.account?.login ?? - `GitHub ${integration.id}`, - repository, - })), - ) - .sort((left, right) => left.repository.localeCompare(right.repository)); -} - -export function buildUserRepositoryOptions( - integrations: ReadonlyArray, - repositoriesByInstallation: Readonly>, -): RepositoryOption[] { - return integrations - .flatMap((integration) => - (repositoriesByInstallation[integration.installation_id] ?? []).map( - (repository) => ({ - integrationId: Number(integration.installation_id), - integrationLabel: - integration.account?.name ?? - `GitHub ${integration.installation_id}`, - repository, - }), - ), - ) - .sort((left, right) => left.repository.localeCompare(right.repository)); -} - -export function repositoryOptionsEqual( - left: ReadonlyArray, - right: ReadonlyArray, -): boolean { - return ( - left.length === right.length && - left.every((option, index) => { - const other = right[index]; - return ( - other?.integrationId === option.integrationId && - other.integrationLabel === option.integrationLabel && - other.repository === option.repository - ); - }) - ); -} - -export function findRepositoryOption( - options: ReadonlyArray, - selection: RepositorySelection, -): RepositoryOption | null { - if (!selection.integrationId || !selection.repository) return null; - return ( - options.find( - (option) => - option.integrationId === selection.integrationId && - option.repository === selection.repository, - ) ?? null - ); -} - -export function toRepositorySelection( - option: RepositoryOption | null, -): RepositorySelection { - return { - integrationId: option?.integrationId ?? null, - repository: option?.repository ?? null, - }; -} - -export function isRepositorySelectionComplete( - selection: RepositorySelection, -): boolean { - return !!selection.integrationId && !!selection.repository; -} - export interface TeamRepositoriesResult { integrationId: number; repos?: string[] | null; diff --git a/packages/core/src/sessions/sessionActivity.ts b/packages/core/src/sessions/sessionActivity.ts index 6e2804ff47..69b5667ff1 100644 --- a/packages/core/src/sessions/sessionActivity.ts +++ b/packages/core/src/sessions/sessionActivity.ts @@ -1,17 +1,25 @@ import { isNotification, POSTHOG_NOTIFICATIONS } from "./acpNotifications"; import type { - PortableSessionEvent, PortableSessionNotification, PortableSessionToolCallStatus, + PortableSessionUpdateEvent, } from "./portableSessionEvents"; export type SessionActivityPhase = "idle" | "connecting" | "working"; +export type SessionActivityEvent = + | PortableSessionUpdateEvent + | { + type: "acp_message"; + ts: number; + message: unknown; + }; + export interface SessionActivityState { isPromptPending?: boolean; awaitingAgentOutput?: boolean; terminalStatus?: "failed" | "completed"; - events?: readonly PortableSessionEvent[]; + events?: readonly SessionActivityEvent[]; } function isQuestionNotification( @@ -44,7 +52,7 @@ function isPendingQuestionStatus( } export function isSessionAwaitingUserInput( - events: readonly PortableSessionEvent[] = [], + events: readonly SessionActivityEvent[] = [], ): boolean { let awaitingUserInput = false; const questionStatuses = new Map< @@ -87,7 +95,13 @@ export function isSessionAwaitingUserInput( continue; } - const method = "method" in event.message ? event.message.method : undefined; + const method = + typeof event.message === "object" && + event.message !== null && + "method" in event.message && + typeof event.message.method === "string" + ? event.message.method + : undefined; if (method === "_posthog/awaiting_user_input") { awaitingUserInput = true; continue; @@ -107,7 +121,7 @@ export function isSessionAwaitingUserInput( } export function countUserMessages( - events: readonly PortableSessionEvent[] = [], + events: readonly SessionActivityEvent[] = [], ): number { return events.filter( (event) => diff --git a/packages/core/src/task-detail/composerControls.test.ts b/packages/core/src/task-detail/composerControls.test.ts deleted file mode 100644 index 0ba02d02cc..0000000000 --- a/packages/core/src/task-detail/composerControls.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { resolveComposerPrimaryAction } from "./composerControls"; - -describe("resolveComposerPrimaryAction", () => { - it.each([ - [{ hasContent: true }, "send"], - [{ canStop: true }, "stop"], - [{ canStop: true, hasContent: true }, "send"], - [{ canStop: true, hasContent: true, allowSendWhileRunning: false }, "stop"], - [{ isRecording: true }, "mic-stop"], - [{}, "mic"], - [{ disabled: true, hasContent: true }, "disabled"], - [{ isTranscribing: true }, "disabled"], - ])("derives %s", (overrides, expected) => { - expect( - resolveComposerPrimaryAction({ - hasContent: false, - disabled: false, - isRecording: false, - isTranscribing: false, - canStop: false, - allowSendWhileRunning: true, - ...overrides, - }), - ).toBe(expected); - }); -}); diff --git a/packages/core/src/task-detail/composerControls.ts b/packages/core/src/task-detail/composerControls.ts deleted file mode 100644 index e9694cccef..0000000000 --- a/packages/core/src/task-detail/composerControls.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - type Adapter, - type CloudTaskConfigOption, - DEFAULT_REASONING_EFFORT, - isRestrictedModelOption, - isSupportedReasoningEffort, - type SupportedReasoningEffort, -} from "@posthog/shared"; - -export interface ComposerModelOption { - value: string; - label: string; - description?: string; - disabled: boolean; -} - -export function getModelConfigOption( - configOptions: readonly CloudTaskConfigOption[], -): CloudTaskConfigOption { - const option = configOptions.find((item) => item.category === "model"); - if (!option) throw new Error("Cloud task model configuration is unavailable"); - return option; -} - -export function getComposerModelOptions( - modelOption: CloudTaskConfigOption, -): ComposerModelOption[] { - return modelOption.options.map((option) => ({ - value: option.value, - label: option.name, - description: option.description, - disabled: isRestrictedModelOption(option._meta), - })); -} - -export function getConfigOptionLabel( - options: ReadonlyArray<{ value: string; name: string }>, - value: string | undefined, -): string | undefined { - return options.find((option) => option.value === value)?.name ?? value; -} - -export function resolveAvailableModel( - modelOption: CloudTaskConfigOption, - value: string, -): string { - const selected = modelOption.options.find((option) => option.value === value); - return selected && !isRestrictedModelOption(selected._meta) - ? value - : modelOption.currentValue; -} - -export function resolveComposerModelChange({ - adapter, - modelOption, - requestedModel, - reasoning, -}: { - adapter: Adapter; - modelOption: CloudTaskConfigOption; - requestedModel: string; - reasoning: SupportedReasoningEffort; -}): { model: string; reasoning: SupportedReasoningEffort } { - const model = resolveAvailableModel(modelOption, requestedModel); - return { - model, - reasoning: isSupportedReasoningEffort(adapter, model, reasoning) - ? reasoning - : DEFAULT_REASONING_EFFORT, - }; -} - -export type ComposerPrimaryAction = - | "send" - | "stop" - | "mic" - | "mic-stop" - | "disabled"; - -export function resolveComposerPrimaryAction({ - hasContent, - disabled, - isRecording, - isTranscribing, - canStop, - allowSendWhileRunning, -}: { - hasContent: boolean; - disabled: boolean; - isRecording: boolean; - isTranscribing: boolean; - canStop: boolean; - allowSendWhileRunning: boolean; -}): ComposerPrimaryAction { - if (disabled || isTranscribing) return "disabled"; - if (canStop && (!allowSendWhileRunning || !hasContent)) return "stop"; - if (hasContent && !isRecording) return "send"; - return isRecording ? "mic-stop" : "mic"; -} diff --git a/packages/core/src/tasks/pendingPrompts.test.ts b/packages/core/src/tasks/pendingPrompts.test.ts index 8a733ad113..28d54e2147 100644 --- a/packages/core/src/tasks/pendingPrompts.test.ts +++ b/packages/core/src/tasks/pendingPrompts.test.ts @@ -3,7 +3,6 @@ import { buildPendingPromptKey, capPendingPrompts, listPendingPromptsNewestFirst, - selectNewestPendingPrompt, } from "./pendingPrompts"; describe("pending prompts", () => { @@ -20,12 +19,11 @@ describe("pending prompts", () => { ).toEqual({ middle: { createdAt: 2 }, newest: { createdAt: 3 } }); }); - it("orders prompts newest first and selects the newest", () => { + it("orders prompts newest first", () => { const prompts = { old: { createdAt: 1 }, new: { createdAt: 2 } }; expect( listPendingPromptsNewestFirst(prompts).map(({ key }) => key), ).toEqual(["new", "old"]); - expect(selectNewestPendingPrompt(prompts)?.key).toBe("new"); }); it.each([ diff --git a/packages/core/src/tasks/pendingPrompts.ts b/packages/core/src/tasks/pendingPrompts.ts index e4f77eafa2..97a90c8324 100644 --- a/packages/core/src/tasks/pendingPrompts.ts +++ b/packages/core/src/tasks/pendingPrompts.ts @@ -32,12 +32,6 @@ export function listPendingPromptsNewestFirst< .sort((left, right) => right.prompt.createdAt - left.prompt.createdAt); } -export function selectNewestPendingPrompt< - TPrompt extends TimestampedPendingPrompt, ->(byKey: Record): RecoverablePendingPrompt | null { - return listPendingPromptsNewestFirst(byKey)[0] ?? null; -} - export function buildPendingPromptKey( randomUuid: string | null, timestamp: number, diff --git a/packages/core/src/tasks/taskActivity.ts b/packages/core/src/tasks/taskActivity.ts index 6a52168e63..50d639e95c 100644 --- a/packages/core/src/tasks/taskActivity.ts +++ b/packages/core/src/tasks/taskActivity.ts @@ -1,9 +1,19 @@ -import { isContentlessTask, type Task } from "@posthog/shared/domain-types"; +import { isContentlessTask } from "@posthog/shared/domain-types"; export type TaskActivitySortMode = "created" | "updated"; +export interface TaskActivityInput { + title: string; + slug: string; + description?: string | null; + internal?: boolean; + created_at: string; + updated_at?: string | null; + latest_run?: { updated_at: string }; +} + export function taskActivityTimestamp( - task: Pick, + task: Pick, sortMode: TaskActivitySortMode, ): number { if (sortMode === "created") { @@ -17,12 +27,12 @@ export function taskActivityTimestamp( ); } -export function filterAndSortTasks( - tasks: readonly Task[], +export function filterAndSortTasks( + tasks: readonly TaskType[], sortMode: TaskActivitySortMode, showInternal: boolean, filter: string, -): Task[] { +): TaskType[] { const normalizedFilter = filter.toLowerCase(); return tasks diff --git a/packages/core/src/tasks/taskArchive.test.ts b/packages/core/src/tasks/taskArchive.test.ts index ab22bbd459..08da2d2eaf 100644 --- a/packages/core/src/tasks/taskArchive.test.ts +++ b/packages/core/src/tasks/taskArchive.test.ts @@ -35,6 +35,10 @@ describe("isTaskRunning", () => { }, ); + it("returns true for the legacy started status", () => { + expect(isTaskRunning({ latest_run: { status: "started" } })).toBe(true); + }); + it.each(["completed", "failed", "cancelled"] as const)( "returns false for %s", (status) => { diff --git a/packages/core/src/tasks/taskArchive.ts b/packages/core/src/tasks/taskArchive.ts index db52bb6310..d5365592cf 100644 --- a/packages/core/src/tasks/taskArchive.ts +++ b/packages/core/src/tasks/taskArchive.ts @@ -1,6 +1,8 @@ -import { isTerminalStatus, type Task } from "@posthog/shared/domain-types"; +import { isTerminalStatus } from "@posthog/shared/domain-types"; -export function isTaskRunning(task: Pick): boolean { +export function isTaskRunning(task: { + latest_run?: { status: string | null }; +}): boolean { const status = task.latest_run?.status; return status !== undefined && !isTerminalStatus(status); } diff --git a/packages/core/src/tasks/taskStatusPresentation.test.ts b/packages/core/src/tasks/taskStatusPresentation.test.ts deleted file mode 100644 index a7895cf59e..0000000000 --- a/packages/core/src/tasks/taskStatusPresentation.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { Task, TaskRun } from "@posthog/shared/domain-types"; -import { describe, expect, it } from "vitest"; -import { getTaskStatusPresentationKind } from "./taskStatusPresentation"; - -function makeTask(latestRun?: Partial): Pick { - return { - latest_run: latestRun - ? { - id: "run-1", - task: "task-1", - team: 1, - branch: null, - status: "not_started", - log_url: "", - error_message: null, - output: null, - state: {}, - created_at: "2026-01-01T00:00:00Z", - updated_at: "2026-01-01T00:00:00Z", - completed_at: null, - ...latestRun, - } - : undefined, - }; -} - -describe("getTaskStatusPresentationKind", () => { - it("prioritizes a pull request over cloud presentation", () => { - expect( - getTaskStatusPresentationKind( - makeTask({ - environment: "cloud", - status: "in_progress", - output: { pr_url: "https://github.com/PostHog/code/pull/123" }, - }), - ), - ).toBe("pr"); - }); - - it.each([ - "not_started", - "queued", - "in_progress", - "completed", - "failed", - "cancelled", - ] as const)("uses chat presentation for cloud status %s", (status) => { - expect( - getTaskStatusPresentationKind(makeTask({ environment: "cloud", status })), - ).toBe("chat"); - }); - - it.each([ - ["completed", "completed"], - ["failed", "failed"], - ["in_progress", "running"], - ["queued", "started"], - ["not_started", "chat"], - ["cancelled", "chat"], - ] as const)("maps local status %s to %s", (status, expected) => { - expect( - getTaskStatusPresentationKind(makeTask({ environment: "local", status })), - ).toBe(expected); - }); - - it("falls back to chat when a task has no run", () => { - expect(getTaskStatusPresentationKind(makeTask())).toBe("chat"); - }); -}); diff --git a/packages/core/src/tasks/taskStatusPresentation.ts b/packages/core/src/tasks/taskStatusPresentation.ts deleted file mode 100644 index 968a17bb76..0000000000 --- a/packages/core/src/tasks/taskStatusPresentation.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { readPrUrls } from "@posthog/shared"; -import type { Task } from "@posthog/shared/domain-types"; - -export type TaskStatusPresentationKind = - | "pr" - | "completed" - | "failed" - | "running" - | "started" - | "chat"; - -export function getTaskStatusPresentationKind( - task: Pick, -): TaskStatusPresentationKind { - const latestRun = task.latest_run; - - if (readPrUrls(latestRun?.output)[0]) { - return "pr"; - } - - if (latestRun?.environment === "cloud") { - return "chat"; - } - - switch (latestRun?.status) { - case "completed": - return "completed"; - case "failed": - return "failed"; - case "in_progress": - return "running"; - case "queued": - return "started"; - default: - return "chat"; - } -} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 043cc19625..b0c6139a74 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -329,18 +329,6 @@ export { stripFrontmatter, } from "./skills"; export type { PostHogAPIConfig } from "./task"; -export { - type CreateTaskAutomationOptions, - createTaskAutomationSchema, - type TaskAutomation, - type TaskAutomationList, - type TaskAutomationValidationErrorDetails, - taskAutomationListSchema, - taskAutomationSchema, - taskAutomationValidationErrorSchema, - type UpdateTaskAutomationOptions, - updateTaskAutomationSchema, -} from "./task-automation"; export type { TaskCreationInput, TaskCreationOutput, diff --git a/packages/shared/src/task-automation.test.ts b/packages/shared/src/task-automation.test.ts deleted file mode 100644 index 57e6fcaa97..0000000000 --- a/packages/shared/src/task-automation.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { describe, expect, expectTypeOf, it } from "vitest"; -import { - type CreateTaskAutomationOptions, - createTaskAutomationSchema, - taskAutomationSchema, - taskAutomationValidationErrorSchema, - type UpdateTaskAutomationOptions, - updateTaskAutomationSchema, -} from "./task-automation"; - -describe("task automation contracts", () => { - it("normalizes optional automation response fields", () => { - expect( - taskAutomationSchema.parse({ - id: "automation-1", - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - cron_expression: "0 9 * * *", - last_run_at: null, - last_run_status: null, - last_task_id: null, - last_task_run_id: null, - last_error: null, - created_at: "2026-07-21T00:00:00Z", - updated_at: "2026-07-21T00:00:00Z", - }), - ).toMatchObject({ - github_integration: null, - timezone: null, - template_id: null, - enabled: true, - }); - }); - - it("keeps create fields required and update fields partial", () => { - const create = createTaskAutomationSchema.parse({ - name: "Daily PRs", - prompt: "Check PRs", - repository: "posthog/posthog", - cron_expression: "0 9 * * *", - timezone: "Europe/London", - }); - const update = updateTaskAutomationSchema.parse({ enabled: false }); - - expect(create.timezone).toBe("Europe/London"); - expect(update).toEqual({ enabled: false }); - expectTypeOf(create).toEqualTypeOf(); - expectTypeOf(update).toEqualTypeOf(); - }); - - it("preserves backend validation field attribution", () => { - expect( - taskAutomationValidationErrorSchema.parse({ - type: "validation_error", - detail: "Enter a valid cron expression.", - attr: "cron_expression", - }), - ).toEqual({ - type: "validation_error", - code: "invalid_input", - detail: "Enter a valid cron expression.", - attr: "cron_expression", - }); - }); -}); diff --git a/packages/shared/src/task-automation.ts b/packages/shared/src/task-automation.ts deleted file mode 100644 index 0f3e4a4bde..0000000000 --- a/packages/shared/src/task-automation.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { z } from "zod"; - -export const taskAutomationSchema = z.object({ - id: z.string(), - name: z.string(), - prompt: z.string(), - repository: z.string(), - github_integration: z.number().nullable().default(null), - cron_expression: z.string(), - timezone: z.string().nullable().default(null), - template_id: z.string().nullable().default(null), - enabled: z.boolean().default(true), - last_run_at: z.string().nullable(), - last_run_status: z.string().nullable(), - last_task_id: z.string().nullable(), - last_task_run_id: z.string().nullable(), - last_error: z.string().nullable(), - created_at: z.string(), - updated_at: z.string(), -}); -export type TaskAutomation = z.infer; - -export const taskAutomationListSchema = z.object({ - count: z.number(), - next: z.string().nullable().optional(), - previous: z.string().nullable().optional(), - results: z.array(taskAutomationSchema), -}); -export type TaskAutomationList = z.infer; - -export const createTaskAutomationSchema = z.object({ - name: z.string(), - prompt: z.string(), - repository: z.string(), - github_integration: z.number().nullable().optional(), - cron_expression: z.string(), - timezone: z.string(), - template_id: z.string().nullable().optional(), - enabled: z.boolean().optional(), -}); -export type CreateTaskAutomationOptions = z.infer< - typeof createTaskAutomationSchema ->; - -export const updateTaskAutomationSchema = createTaskAutomationSchema.partial(); -export type UpdateTaskAutomationOptions = z.infer< - typeof updateTaskAutomationSchema ->; - -export const taskAutomationValidationErrorSchema = z.object({ - type: z.string().optional(), - code: z.string().default("invalid_input"), - detail: z.string(), - attr: z.string().nullable().default(null), -}); -export type TaskAutomationValidationErrorDetails = z.infer< - typeof taskAutomationValidationErrorSchema ->; diff --git a/packages/ui/src/features/inbox/hooks/useTrackInboxViewed.ts b/packages/ui/src/features/inbox/hooks/useTrackInboxViewed.ts index da5f0a9a4b..f586f5fcbf 100644 --- a/packages/ui/src/features/inbox/hooks/useTrackInboxViewed.ts +++ b/packages/ui/src/features/inbox/hooks/useTrackInboxViewed.ts @@ -41,6 +41,7 @@ export function useTrackInboxViewed(): void { totalCount, tabCounts: counts, filters: { + surface: "desktop", sourceProductFilter, priorityFilter, searchQuery, diff --git a/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx b/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx index d34dbd9135..34f979a2b0 100644 --- a/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx +++ b/packages/ui/src/features/sessions/components/UnifiedModelSelector.tsx @@ -9,7 +9,6 @@ import { Robot, Spinner, } from "@phosphor-icons/react"; -import { getConfigOptionLabel } from "@posthog/core/task-detail/composerControls"; import { Button, DropdownMenu, @@ -79,7 +78,9 @@ export function UnifiedModelSelector({ }, [selectOption]); const currentValue = selectOption?.currentValue; - const currentLabel = getConfigOptionLabel(options, currentValue); + const currentLabel = + options.find((option) => option.value === currentValue)?.name ?? + currentValue; const otherAdapter = getOtherAdapter(adapter); From 38e8dc79124db146c614cdbe93971d807c0aa6fa Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:37:39 +0300 Subject: [PATCH 32/42] fix(mobile): align portable runtime dependencies Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/code/package.json | 4 +- apps/mobile/package.json | 5 +- apps/web/package.json | 4 +- pnpm-lock.yaml | 2767 +++++++++++++++++++------------------- pnpm-workspace.yaml | 14 +- 5 files changed, 1415 insertions(+), 1379 deletions(-) diff --git a/apps/code/package.json b/apps/code/package.json index 16f340aab8..b18e2e865c 100644 --- a/apps/code/package.json +++ b/apps/code/package.json @@ -145,8 +145,8 @@ "node-pty": "1.1.0", "posthog-node": "^5.35.6", "radix-themes-tw": "0.2.3", - "react": "19.2.6", - "react-dom": "19.2.6", + "react": "19.1.0", + "react-dom": "19.1.0", "react-hotkeys-hook": "^4.4.4", "react-scan": "^0.5.6", "reflect-metadata": "^0.2.2", diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 09ebacf3d0..d49b3e47a0 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -66,7 +66,8 @@ "phosphor-react-native": "^3.0.2", "posthog-react-native": "^4.18.0", "posthog-react-native-session-replay": "^1.6.0", - "react": "19.2.6", + "react": "19.1.0", + "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-keyboard-controller": "1.18.5", "react-native-reanimated": "~4.1.1", @@ -83,7 +84,7 @@ "@types/react-test-renderer": "^19.1.0", "@vitejs/plugin-react": "^4.7.0", "react-native-svg-transformer": "^1.5.3", - "react-test-renderer": "^19.2.6", + "react-test-renderer": "19.1.0", "tailwindcss": "^3.4.18", "typescript": "~5.9.2", "vite": "^6.4.1", diff --git a/apps/web/package.json b/apps/web/package.json index bf1eff1d71..f710ff6c8b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -29,8 +29,8 @@ "@trpc/server": "^11.17.0", "@trpc/tanstack-react-query": "^11.17.0", "inversify": "^7.10.6", - "react": "19.2.6", - "react-dom": "19.2.6", + "react": "19.1.0", + "react-dom": "19.1.0", "reflect-metadata": "^0.2.2", "superjson": "catalog:", "zod": "^4.4.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4f35ccf04a..ffdb47c73a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,9 +85,9 @@ catalogs: overrides: node-abi: ^3.92.0 zod@^4.0.0: 4.4.3 - react: 19.2.6 - react-dom: 19.2.6 - react-test-renderer: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 + react-test-renderer: 19.1.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 '@posthog/quill>@base-ui/react': ^1.3.0 @@ -165,10 +165,10 @@ importers: version: 2.5.6 '@phosphor-icons/react': specifier: ^2.1.10 - version: 2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/agent': specifier: workspace:* version: link:../../packages/agent @@ -192,7 +192,7 @@ importers: version: link:../../packages/git '@posthog/hedgehog-mode': specifier: ^0.0.53 - version: 0.0.53(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.0.53(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/host-router': specifier: workspace:* version: link:../../packages/host-router @@ -204,7 +204,7 @@ importers: version: link:../../packages/platform '@posthog/quill': specifier: 'catalog:' - version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1) + version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.3.1) '@posthog/shared': specifier: workspace:* version: link:../../packages/shared @@ -219,16 +219,16 @@ importers: version: link:../../packages/workspace-server '@radix-ui/themes': specifier: ^3.2.1 - version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tailwindcss/vite': specifier: ^4.3.0 version: 4.3.1(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) '@tanstack/react-query': specifier: ^5.100.14 - version: 5.101.0(react@19.2.6) + version: 5.101.0(react@19.1.0) '@tanstack/router-plugin': specifier: ^1.168.13 - version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@trpc/client': specifier: ^11.17.0 version: 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) @@ -237,7 +237,7 @@ importers: version: 11.17.0(typescript@5.9.3) '@trpc/tanstack-react-query': specifier: ^11.17.0 - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) better-sqlite3: specifier: ^12.10.1 version: 12.10.1 @@ -299,17 +299,17 @@ importers: specifier: 0.2.3 version: 0.2.3 react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 react-dom: - specifier: 19.2.6 - version: 19.2.6(react@19.2.6) + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) react-hotkeys-hook: specifier: ^4.4.4 - version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-scan: specifier: ^0.5.6 - version: 0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.57.1) + version: 0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.57.1) reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -346,16 +346,16 @@ importers: version: 1.4.5(rollup@4.57.1) '@storybook/addon-a11y': specifier: 10.4.1 - version: 10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + version: 10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@storybook/addon-docs': specifier: 10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@storybook/react-vite': specifier: 10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@storybook/test-runner': specifier: ^0.24.4 - version: 0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + version: 0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@tanstack/devtools-vite': specifier: ^0.8.1 version: 0.8.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) @@ -364,7 +364,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) @@ -439,7 +439,7 @@ importers: version: 8.5.15 storybook: specifier: 10.4.1 - version: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwindcss: specifier: ^4.3.0 version: 4.3.1 @@ -448,7 +448,7 @@ importers: version: 4.22.4 typed-openapi: specifier: ^2.2.6 - version: 2.2.7(openapi-types@12.1.3)(react@19.2.6) + version: 2.2.7(openapi-types@12.1.3)(react@19.1.0) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -472,10 +472,10 @@ importers: dependencies: '@expo/ui': specifier: 0.2.0-beta.9 - version: 0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) '@modelcontextprotocol/ext-apps': specifier: ^1.2.2 - version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3) + version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3) '@modelcontextprotocol/sdk': specifier: ^1.29.0 version: 1.29.0(zod@4.4.3) @@ -490,37 +490,37 @@ importers: version: link:../../packages/shared '@react-native-async-storage/async-storage': specifier: ^2.2.0 - version: 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + version: 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) '@react-native-community/netinfo': specifier: ^12.0.1 - version: 12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) '@tanstack/react-query': specifier: ^5.90.12 - version: 5.90.20(react@19.2.6) + version: 5.90.20(react@19.1.0) date-fns: specifier: ^4.1.0 version: 4.1.0 expo: specifier: ~54.0.27 - version: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-application: specifier: ~7.0.8 version: 7.0.8(expo@54.0.33) expo-auth-session: specifier: ^7.0.10 - version: 7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-av: specifier: ~16.0.8 - version: 16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-camera: specifier: ^55.0.15 - version: 55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-clipboard: specifier: ^55.0.13 - version: 55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-constants: specifier: ~18.0.11 - version: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + version: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) expo-crypto: specifier: ^15.0.8 version: 15.0.8(expo@54.0.33) @@ -535,13 +535,13 @@ importers: version: 14.0.8(expo@54.0.33) expo-file-system: specifier: ~19.0.21 - version: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + version: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) expo-font: specifier: ^14.0.10 - version: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-glass-effect: specifier: ~0.1.8 - version: 0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-haptics: specifier: ^55.0.14 version: 55.0.14(expo@54.0.33) @@ -550,86 +550,89 @@ importers: version: 17.0.11(expo@54.0.33) expo-linear-gradient: specifier: ^15.0.8 - version: 15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-linking: specifier: ~8.0.10 - version: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-localization: specifier: ~17.0.8 - version: 17.0.8(expo@54.0.33)(react@19.2.6) + version: 17.0.8(expo@54.0.33)(react@19.1.0) expo-notifications: specifier: ~0.32.12 - version: 0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-router: specifier: ~6.0.17 - version: 6.0.23(d7377593e8774c4353274c7599e842cf) + version: 6.0.23(76047f2336d892e43bef2ac48cb56303) expo-secure-store: specifier: ^15.0.8 version: 15.0.8(expo@54.0.33) expo-speech-recognition: specifier: ^3.1.2 - version: 3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-splash-screen: specifier: ~31.0.12 version: 31.0.13(expo@54.0.33) expo-status-bar: specifier: ~3.0.9 - version: 3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-system-ui: specifier: ~6.0.9 - version: 6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + version: 6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) expo-web-browser: specifier: ^15.0.10 - version: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + version: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) highlight.js: specifier: ^11.11.1 version: 11.11.1 nativewind: specifier: ^4.2.1 - version: 4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) + version: 4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) phosphor-react-native: specifier: ^3.0.2 - version: 3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) posthog-react-native: specifier: ^4.18.0 - version: 4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)))(expo-localization@17.0.8(expo@54.0.33)(react@19.2.6))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)) + version: 4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(expo-localization@17.0.8(expo@54.0.33)(react@19.1.0))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)) posthog-react-native-session-replay: specifier: ^1.6.0 - version: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 + react-dom: + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) react-native: specifier: 0.81.5 - version: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + version: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) react-native-keyboard-controller: specifier: 1.18.5 - version: 1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react-native-reanimated: specifier: ~4.1.1 - version: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react-native-safe-area-context: specifier: ~5.6.2 - version: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react-native-screens: specifier: ~4.16.0 - version: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react-native-svg: specifier: ^15.15.1 - version: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) react-native-web: specifier: ^0.21.2 - version: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-native-webview: specifier: ^13.13.5 - version: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + version: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6) + version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0) devDependencies: '@testing-library/react-native': specifier: ^13.3.3 - version: 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6) + version: 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) '@types/react': specifier: ^19.2.15 version: 19.2.17 @@ -641,10 +644,10 @@ importers: version: 4.7.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@1.21.7)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) react-native-svg-transformer: specifier: ^1.5.3 - version: 1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(typescript@5.9.3) + version: 1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(typescript@5.9.3) react-test-renderer: - specifier: 19.2.6 - version: 19.2.6(react@19.2.6) + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) tailwindcss: specifier: ^3.4.18 version: 3.4.19(tsx@4.22.4)(yaml@2.9.0) @@ -668,7 +671,7 @@ importers: version: 2.2.0(inversify@7.11.0(reflect-metadata@0.2.2)) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/agent': specifier: workspace:* version: link:../../packages/agent @@ -698,7 +701,7 @@ importers: version: link:../../packages/workspace-client '@tanstack/react-query': specifier: ^5.100.14 - version: 5.101.0(react@19.2.6) + version: 5.101.0(react@19.1.0) '@trpc/client': specifier: ^11.17.0 version: 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) @@ -707,16 +710,16 @@ importers: version: 11.17.0(typescript@5.9.3) '@trpc/tanstack-react-query': specifier: ^11.17.0 - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) inversify: specifier: ^7.10.6 version: 7.11.0(reflect-metadata@0.2.2) react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 react-dom: - specifier: 19.2.6 - version: 19.2.6(react@19.2.6) + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -735,7 +738,7 @@ importers: version: 4.2.2(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) '@tanstack/router-plugin': specifier: 'catalog:' - version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2)) + version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2)) '@types/react': specifier: ^19.2.15 version: 19.2.17 @@ -963,8 +966,8 @@ importers: specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 typescript: specifier: 'catalog:' version: 5.9.3 @@ -1133,16 +1136,16 @@ importers: version: link:../../tooling/typescript '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.2.6) + version: 5.101.0(react@19.1.0) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) '@types/react': specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 typescript: specifier: 'catalog:' version: 5.9.3 @@ -1201,7 +1204,7 @@ importers: version: 0.22.1(zod@4.4.3) '@base-ui/react': specifier: ^1.3.0 - version: 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@codemirror/lang-angular': specifier: ^0.1.4 version: 0.1.4 @@ -1279,7 +1282,7 @@ importers: version: 0.1.21 '@dnd-kit/react': specifier: ^0.1.21 - version: 0.1.21(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.1.21(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@joplin/turndown-plugin-gfm': specifier: ^1.0.67 version: 1.0.67 @@ -1288,7 +1291,7 @@ importers: version: 0.19.0(zod@4.4.3) '@json-render/react': specifier: ^0.19.0 - version: 0.19.0(react@19.2.6)(zod@4.4.3) + version: 0.19.0(react@19.1.0)(zod@4.4.3) '@lezer/common': specifier: ^1.5.1 version: 1.5.1 @@ -1297,13 +1300,13 @@ importers: version: 1.2.3 '@modelcontextprotocol/ext-apps': specifier: ^1.1.2 - version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3) + version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3) '@modelcontextprotocol/sdk': specifier: ^1.12.1 version: 1.29.0(zod@4.4.3) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/agent': specifier: workspace:* version: link:../agent @@ -1327,7 +1330,7 @@ importers: version: link:../platform '@posthog/quill-charts': specifier: 0.3.0-beta.19 - version: 0.3.0-beta.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.3.0-beta.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/shared': specifier: workspace:* version: link:../shared @@ -1336,22 +1339,22 @@ importers: version: link:../workspace-client '@radix-ui/react-collapsible': specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-icons': specifier: ^1.3.2 - version: 1.3.2(react@19.2.6) + version: 1.3.2(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-router': specifier: 'catalog:' - version: 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-router-devtools': specifier: 'catalog:' - version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-virtual': specifier: ^3.13.26 - version: 3.14.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 3.14.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tiptap/core': specifier: ^3.13.0 version: 3.19.0(@tiptap/pm@3.19.0) @@ -1366,7 +1369,7 @@ importers: version: 3.19.0 '@tiptap/react': specifier: ^3.13.0 - version: 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tiptap/starter-kit': specifier: ^3.13.0 version: 3.19.0 @@ -1375,7 +1378,7 @@ importers: version: 3.19.0(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -1399,10 +1402,10 @@ importers: version: 2.1.1 cmdk: specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) framer-motion: specifier: ^12.26.2 - version: 12.31.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 12.31.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) fuse.js: specifier: ^7.1.0 version: 7.1.0 @@ -1414,7 +1417,7 @@ importers: version: 7.11.0(reflect-metadata@0.2.2) lucide-react: specifier: ^1.7.0 - version: 1.7.0(react@19.2.6) + version: 1.7.0(react@19.1.0) posthog-js: specifier: ^1.378.0 version: 1.386.8 @@ -1423,16 +1426,16 @@ importers: version: 0.2.3 react-hotkeys-hook: specifier: ^4.4.4 - version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.17)(react@19.2.6) + version: 10.1.0(@types/react@19.2.17)(react@19.1.0) react-resizable-panels: specifier: ^3.0.6 - version: 3.0.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 3.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-zoom-pan-pinch: specifier: ^4.0.3 - version: 4.0.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 4.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) reflect-metadata: specifier: 'catalog:' version: 0.2.2 @@ -1465,7 +1468,7 @@ importers: version: 11.0.5 virtua: specifier: ^0.48.6 - version: 0.48.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13) + version: 0.48.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.13) vscode-icons-js: specifier: ^11.6.1 version: 11.6.1 @@ -1474,23 +1477,23 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.0 - version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6) + version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0) devDependencies: '@phosphor-icons/react': specifier: 'catalog:' - version: 2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@posthog/quill': specifier: 'catalog:' - version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2) + version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.2.2) '@posthog/tsconfig': specifier: workspace:* version: link:../../tooling/typescript '@radix-ui/themes': specifier: 'catalog:' - version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.2.6) + version: 5.101.0(react@19.1.0) '@tanstack/router-generator': specifier: 'catalog:' version: 1.167.17 @@ -1499,7 +1502,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) @@ -1525,11 +1528,11 @@ importers: specifier: ^26.0.0 version: 26.1.0 react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 react-dom: - specifier: 19.2.6 - version: 19.2.6(react@19.2.6) + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1554,16 +1557,16 @@ importers: version: link:../workspace-server '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.2.6) + version: 5.101.0(react@19.1.0) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) '@types/react': specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.2.6 - version: 19.2.6 + specifier: 19.1.0 + version: 19.1.0 typescript: specifier: 'catalog:' version: 5.9.3 @@ -2532,8 +2535,8 @@ packages: engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -2542,8 +2545,8 @@ packages: resolution: {integrity: sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -2736,8 +2739,8 @@ packages: '@dnd-kit/react@0.1.21': resolution: {integrity: sha512-fxcr1tWF7+KSNq464ZOGvQETSH9zYb68VOdx8Ie3XoCUnNicJW5YBZrwvMeDhUDnvLS+W2iHiVuUjtXDKJjNeg==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@dnd-kit/state@0.1.21': resolution: {integrity: sha512-pdhntEPvn/QttcF295bOJpWiLsRqA/Iczh1ODOJUxGiR+E4GkYVz9VapNNm9gDq6ST0tr/e1Q2xBztUHlJqQgA==} @@ -3525,7 +3528,7 @@ packages: '@expo/devtools@0.1.8': resolution: {integrity: sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' peerDependenciesMeta: react: @@ -3558,8 +3561,8 @@ packages: resolution: {integrity: sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==} peerDependencies: expo: '*' - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-native: '*' peerDependenciesMeta: react-dom: @@ -3600,14 +3603,14 @@ packages: resolution: {integrity: sha512-RaBcp0cMe5GykQogJwRZGy4o4JHDLtrr+HaurDPhwPKqVATsV0rR11ysmFe4QX8XWLP/L3od7NOkXUi5ailvaw==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' '@expo/vector-icons@15.0.3': resolution: {integrity: sha512-SBUyYKphmlfUBqxSfDdJ3jAdEVSALS2VUPOUyqn48oZmb2TL/O7t7/PQm5v4NQujYEPLPMTLn9KVw6H7twwbTA==} peerDependencies: expo-font: '>=14.0.4' - react: 19.2.6 + react: 19.1.0 react-native: '*' '@expo/ws-tunnel@1.0.6': @@ -3632,14 +3635,14 @@ packages: '@floating-ui/react-dom@2.1.8': resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@floating-ui/react@0.27.19': resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} @@ -4052,7 +4055,7 @@ packages: '@json-render/react@0.19.0': resolution: {integrity: sha512-kTW6b6cSNRrlEfCUf/69SLoLn+CufC968ruge9tnQlp9pDTGG/SK8pgM541FdgwMFA4zm3s5mpM3G8rdODKc/A==} peerDependencies: - react: 19.2.6 + react: 19.1.0 '@jsonjoy.com/base64@1.1.2': resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} @@ -4314,7 +4317,7 @@ packages: resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 '@mistralai/mistralai@2.2.6': resolution: {integrity: sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==} @@ -4332,8 +4335,8 @@ packages: engines: {node: '>=20'} peerDependencies: '@modelcontextprotocol/sdk': ^1.24.0 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 zod: 4.4.3 peerDependenciesMeta: react: @@ -5627,14 +5630,14 @@ packages: resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} engines: {node: '>=10'} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@pierre/diffs@1.2.10': resolution: {integrity: sha512-rPeAmDWarxFVTQpaf4y6wTxjZxU44xKJKoJti2zU21P06DVd9nRHZX+xSIObLB307Qjpaesyb1x/j0z94t7vLw==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@pierre/theme@1.0.3': resolution: {integrity: sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==} @@ -5645,8 +5648,8 @@ packages: peerDependencies: '@pierre/theme': ^1.0.0 '@shikijs/themes': ^3.0.0 || ^4.0.0 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 shiki: ^3.0.0 || ^4.0.0 peerDependenciesMeta: '@pierre/theme': @@ -5705,8 +5708,8 @@ packages: resolution: {integrity: sha512-Qyd9DckDg1Z/vT3mpKyuMemJHWpYD0k0Gob7hWCNMCDSYm/NcpDS1uX8PRoh3Z7HF2kBkZ7j6HCSUIG7Ha/j4Q==} engines: {node: '>=18'} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@posthog/plugin-utils@1.1.1': resolution: {integrity: sha512-vCbaFeuwf9Pc0gI5bkCGvkOn2Bxru2KbZJtOa6loTJjanCNoMsjECEPijr7X5oln1IIg+VKnGiwV4tKY2b7NuQ==} @@ -5714,16 +5717,16 @@ packages: '@posthog/quill-charts@0.3.0-beta.19': resolution: {integrity: sha512-SqZQr+zclHTjdCeZQh+mrH9nzZk1dFDPC9++1QYh0IdMa/dEFzxCkQgIuY7BpRsPv4YIB5WlTIB4XT/BujR2xA==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@posthog/quill@0.3.0-beta.24': resolution: {integrity: sha512-lBnnFqX3aVNXPPc5j8pO2cGr99IeClIr2ByVTdote477Bnqwt8HDX7jbFxCwiUr8ARnuSTvhDrqeagZzplwE9Q==} engines: {node: '>=20'} peerDependencies: '@base-ui/react': ^1.3.0 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 tailwindcss: ^4.0.0 '@posthog/rollup-plugin@1.4.5': @@ -5792,8 +5795,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5805,8 +5808,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5818,8 +5821,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5831,8 +5834,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5844,8 +5847,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5857,8 +5860,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5870,8 +5873,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5883,8 +5886,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5896,8 +5899,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5908,7 +5911,7 @@ packages: resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5918,8 +5921,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5930,7 +5933,7 @@ packages: resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5940,8 +5943,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5952,7 +5955,7 @@ packages: resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5962,8 +5965,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5975,8 +5978,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5987,7 +5990,7 @@ packages: resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -5997,8 +6000,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6010,8 +6013,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6023,8 +6026,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6034,13 +6037,13 @@ packages: '@radix-ui/react-icons@1.3.2': resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} peerDependencies: - react: 19.2.6 + react: 19.1.0 '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6050,8 +6053,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6063,8 +6066,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6076,8 +6079,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6089,8 +6092,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6102,8 +6105,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6115,8 +6118,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6128,8 +6131,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6141,8 +6144,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6154,8 +6157,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6167,8 +6170,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6180,8 +6183,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6193,8 +6196,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6206,8 +6209,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6219,8 +6222,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6232,8 +6235,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6245,8 +6248,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6258,8 +6261,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6271,8 +6274,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6284,8 +6287,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6296,7 +6299,7 @@ packages: resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6305,7 +6308,7 @@ packages: resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6314,7 +6317,7 @@ packages: resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6324,8 +6327,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6337,8 +6340,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6350,8 +6353,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6363,8 +6366,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6376,8 +6379,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6389,8 +6392,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6402,8 +6405,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6414,7 +6417,7 @@ packages: resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6423,7 +6426,7 @@ packages: resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6432,7 +6435,7 @@ packages: resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6441,7 +6444,7 @@ packages: resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6450,7 +6453,7 @@ packages: resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6459,7 +6462,7 @@ packages: resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6468,7 +6471,7 @@ packages: resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6477,7 +6480,7 @@ packages: resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6486,7 +6489,7 @@ packages: resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6496,8 +6499,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6512,8 +6515,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -6532,7 +6535,7 @@ packages: '@react-native-community/netinfo@12.0.1': resolution: {integrity: sha512-P/3caXIvfYSJG8AWJVefukg+ZGRPs+M4Lp3pNJtgcTYoJxCjWrKQGNnCkj/Cz//zWa/avGed0i/wzm0T8vV2IQ==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '>=0.59' '@react-native/assets-registry@0.81.5': @@ -6594,7 +6597,7 @@ packages: engines: {node: '>= 20.19.4'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 react-native: '*' peerDependenciesMeta: '@types/react': @@ -6604,7 +6607,7 @@ packages: resolution: {integrity: sha512-/GtOfVWRligHG0mvX39I1FGdUWeWl0GVF2okEziQSQj0bOTrLIt7y44C3r/aCLkEpTVltCPGM3swqGTH3UfRCw==} peerDependencies: '@react-navigation/native': ^7.1.28 - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' @@ -6612,14 +6615,14 @@ packages: '@react-navigation/core@7.14.0': resolution: {integrity: sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g==} peerDependencies: - react: 19.2.6 + react: 19.1.0 '@react-navigation/elements@2.9.5': resolution: {integrity: sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==} peerDependencies: '@react-native-masked-view/masked-view': '>= 0.2.0' '@react-navigation/native': ^7.1.28 - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-safe-area-context: '>= 4.0.0' peerDependenciesMeta: @@ -6630,7 +6633,7 @@ packages: resolution: {integrity: sha512-XmNJsPshjkNsahgbxNgGWQUq4s1l6HqH/Fei4QsjBNn/0mTvVrRVZwJ1XrY9YhWYvyiYkAN6/OmarWQaQJ0otQ==} peerDependencies: '@react-navigation/native': ^7.1.28 - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-safe-area-context: '>= 4.0.0' react-native-screens: '>= 4.0.0' @@ -6638,7 +6641,7 @@ packages: '@react-navigation/native@7.1.28': resolution: {integrity: sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' '@react-navigation/routers@7.5.3': @@ -7087,16 +7090,16 @@ packages: '@storybook/icons@2.0.2': resolution: {integrity: sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@storybook/react-dom-shim@10.4.1': resolution: {integrity: sha512-6QFqfDNH4DMrt7yHKRfpqRopsVUc/Az+sXIdJ39IetYnHUxL3nW4NVaPc6uy/8Qi8urzUyEXL/nn7cpSIP2aPQ==} peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 storybook: ^10.4.1 peerDependenciesMeta: '@types/react': @@ -7107,8 +7110,8 @@ packages: '@storybook/react-vite@10.4.1': resolution: {integrity: sha512-zY6OzaXvXqBIUyc5ySE55/LAPQiF+o9ZyhQI978WMu4mY/fL7FpQ+ZVHRUCCgz/wTXtqE9jJwd/N10HI1kD0/Q==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 storybook: ^10.4.1 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -7117,8 +7120,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 storybook: ^10.4.1 typescript: '>= 4.9.x' peerDependenciesMeta: @@ -7534,12 +7537,12 @@ packages: '@tanstack/react-query@5.101.0': resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 '@tanstack/react-query@5.90.20': resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==} peerDependencies: - react: 19.2.6 + react: 19.1.0 '@tanstack/react-router-devtools@1.167.0': resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} @@ -7547,8 +7550,8 @@ packages: peerDependencies: '@tanstack/react-router': ^1.170.0 '@tanstack/router-core': ^1.170.0 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@tanstack/router-core': optional: true @@ -7557,20 +7560,20 @@ packages: resolution: {integrity: sha512-GawYz7HEjj8rTUUDoT/SemDEVm63pZUO+2mOcXHY9Jl3EwMS5gFBnPu/2UvcrwRm1jN1k79fokc0d4aFmrLatg==} engines: {node: '>=20.19'} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@tanstack/react-store@0.9.3': resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@tanstack/react-virtual@3.14.2': resolution: {integrity: sha512-IpWnmCLvuymRfeeLNVXIzNEYBFLpd3drVIS91sqV78VTZFyldlChkOocZRCPp1B+Wnk09bcLNme8WaMU/9/9bQ==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@tanstack/router-core@1.171.13': resolution: {integrity: sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA==} @@ -7638,9 +7641,9 @@ packages: engines: {node: '>=18'} peerDependencies: jest: '>=29.0.0' - react: 19.2.6 + react: 19.1.0 react-native: '>=0.71' - react-test-renderer: 19.2.6 + react-test-renderer: 19.1.0 peerDependenciesMeta: jest: optional: true @@ -7652,8 +7655,8 @@ packages: '@testing-library/dom': ^10.0.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -7821,8 +7824,8 @@ packages: '@tiptap/pm': ^3.19.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 '@tiptap/starter-kit@3.19.0': resolution: {integrity: sha512-dTCkHEz+Y8ADxX7h+xvl6caAj+3nII/wMB1rTQchSuNKqJTOrzyUsCWm094+IoZmLT738wANE0fRIgziNHs/ug==} @@ -7871,7 +7874,7 @@ packages: '@tanstack/react-query': ^5.80.3 '@trpc/client': 11.17.0 '@trpc/server': 11.17.0 - react: 19.2.6 + react: 19.1.0 typescript: '>=5.7.2' '@ts-morph/common@0.27.0': @@ -8839,12 +8842,12 @@ packages: bippy@0.5.42: resolution: {integrity: sha512-K3tpfO9uGQB2k/Vi5P6jgfrnXvO/FAQNUE2tqKjQmT0a93fJCysMGLgJmRKzYYfybAoOtwWwmKm0vw/uXE0hMw==} peerDependencies: - react: 19.2.6 + react: 19.1.0 bippy@0.5.43: resolution: {integrity: sha512-Tvu7b1M7+d8b9/YHaCeODEsi2CgbuoBql+dWSBrNnCuqJ1gMUeY3i0r+319hvjjl5GVBP6FFWxrKnq3fhZER0w==} peerDependencies: - react: 19.2.6 + react: 19.1.0 bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -8883,9 +8886,6 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - brace-expansion@2.1.0: resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} @@ -9153,8 +9153,8 @@ packages: cmdk@1.1.1: resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} @@ -9575,8 +9575,8 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - deslop-js@0.7.8: - resolution: {integrity: sha512-QMmb3Z/ARvYZmZneudb8cnY/4mVvZTdhUyA9TC2skwOcm7KvY9zyOdn0TApQc4rL0VM2TffFkmo3ky/lJZX7qw==} + deslop-js@0.8.3: + resolution: {integrity: sha512-axNV/iX3Zq9xt0MYesmbBGxneeeY/HrYgXTsaM4+GOrdxXP9JyCfTdC4j8zx09bBML94oSIpFNyn9U1f0oEPqQ==} destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} @@ -10250,20 +10250,20 @@ packages: resolution: {integrity: sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-auth-session@7.0.10: resolution: {integrity: sha512-XDnKkudvhHSKkZfJ+KkodM+anQcrxB71i+h0kKabdLa5YDXTQ81aC38KRc3TMqmnBDHAu0NpfbzEVd9WDFY3Qg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-av@16.0.8: resolution: {integrity: sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-web: '*' peerDependenciesMeta: @@ -10274,7 +10274,7 @@ packages: resolution: {integrity: sha512-WRVsZf+2p7EsxudwyiUMYijJS8M98t/BVP6yG7N+08JSUotkGjmZcemom1gM36uy27P8QsSVP0hD+FravmQiBA==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-web: '*' peerDependenciesMeta: @@ -10285,7 +10285,7 @@ packages: resolution: {integrity: sha512-PrOmmuVsGW4bAkNQmGKtxMXj3invsfN+jfIKmQxHwE/dn7ODqwFWviUTa+PMUjP3XZmYCDLyu/i0GLeu7HF9Ew==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-constants@18.0.13: @@ -10339,14 +10339,14 @@ packages: resolution: {integrity: sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-glass-effect@0.1.8: resolution: {integrity: sha512-9Cp17ax0Fpugue8+Bd7Ndl/dSAvGmt4bQ5mQLw9zc1A2lctUse3cEg9nI7TnDJiwKf+A/VAPN6+3K12JVMYgZg==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-haptics@55.0.14: @@ -10371,26 +10371,26 @@ packages: resolution: {integrity: sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 expo-linear-gradient@15.0.8: resolution: {integrity: sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-linking@8.0.11: resolution: {integrity: sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-localization@17.0.8: resolution: {integrity: sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 expo-manifests@1.0.10: resolution: {integrity: sha512-oxDUnURPcL4ZsOBY6X1DGWGuoZgVAFzp6PISWV7lPP2J0r8u1/ucuChBgpK7u1eLGFp6sDIPwXyEUCkI386XSQ==} @@ -10404,14 +10404,14 @@ packages: expo-modules-core@3.0.29: resolution: {integrity: sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-notifications@0.32.17: resolution: {integrity: sha512-lwwzn7tImuzTzn9PAglZlS2VfZEvsfFGJTK9Eb8I4cqkGh2DI23YJFJH+WPEIu4QhDvk5JeBjklenJ8IZbmA4A==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-router@6.0.23: @@ -10423,8 +10423,8 @@ packages: expo: '*' expo-constants: ^18.0.13 expo-linking: ^8.0.11 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-native: '*' react-native-gesture-handler: '*' react-native-reanimated: '*' @@ -10461,7 +10461,7 @@ packages: resolution: {integrity: sha512-yaXy+6w218Urdshits2KsfLjXNCnGNlXzUxEP4BVehKEbiIPAeUKBzuicCeELU5H2zTLwL9u+RjbFAUom4LiYQ==} peerDependencies: expo: '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-splash-screen@31.0.13: @@ -10472,7 +10472,7 @@ packages: expo-status-bar@3.0.9: resolution: {integrity: sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' expo-system-ui@6.0.9: @@ -10502,7 +10502,7 @@ packages: peerDependencies: '@expo/dom-webview': '*' '@expo/metro-runtime': '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-webview: '*' peerDependenciesMeta: @@ -10709,8 +10709,8 @@ packages: resolution: {integrity: sha512-Tnd0FU05zGRFI3JJmBegXonF1rfuzYeuXd1QSdQ99Ysnppk0yWBWSW2wUsqzRpS5nv0zPNx+y0wtDj4kf0q5RQ==} peerDependencies: '@emotion/is-prop-valid': '*' - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@emotion/is-prop-valid': optional: true @@ -10895,10 +10895,6 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@13.0.1: - resolution: {integrity: sha512-B7U/vJpE3DkJ5WXTgTpTRN63uV42DseiXXKMwG14LQBXmsdeIoHAPbU/MEo6II0k5ED74uc2ZGTC6MwHFQhF6w==} - engines: {node: 20 || >=22} - glob@13.0.6: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} @@ -12185,12 +12181,12 @@ packages: lucide-react@0.577.0: resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} peerDependencies: - react: 19.2.6 + react: 19.1.0 lucide-react@1.7.0: resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} @@ -12945,8 +12941,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint-plugin-react-doctor@0.7.8: - resolution: {integrity: sha512-3f9/jFLIC/KRLPYqxiXSk20cq47luGy9Oz5Ru7nK7w0EI9B9zuMvAU92bK9jFiwFE7Lc9PA8ER6Y+naYaGFQGw==} + oxlint-plugin-react-doctor@0.8.3: + resolution: {integrity: sha512-S1Gq1H9+BpziApWcZ/sWPNYYy1FMkFmtSEGiqIJ4/Aac76LfkeutPFtSRlkJF1JlcrbYFf7LXcfcxZCJgmVBBQ==} engines: {node: ^20.19.0 || >=22.13.0} oxlint@1.66.0: @@ -13064,7 +13060,7 @@ packages: resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} engines: {node: '>=14.x'} peerDependencies: - react: 19.2.6 + react: 19.1.0 xstate: '>=4.32.1' peerDependenciesMeta: react: @@ -13101,10 +13097,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.1: - resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} - engines: {node: 20 || >=22} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} @@ -13133,7 +13125,7 @@ packages: phosphor-react-native@3.0.3: resolution: {integrity: sha512-h8UIIG/V4pgm20uvkt7L8G/GsOWKaU7rnyu2jnGt1vKmaigE0GZWXOHoEw9wZcfATHwvUpr/mkubEG/nbKeJkg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-svg: '*' @@ -13291,7 +13283,7 @@ packages: posthog-react-native-session-replay@1.6.0: resolution: {integrity: sha512-OCaei77mtgg7JT+TgHSCgpWeKq2XXENUOPNxGbjhXZa/aJpptOW5VsBqjtH4BPzM2c1veS1DK4/Fb/uV4Rb3cg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' posthog-react-native@4.30.0: @@ -13557,8 +13549,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -13592,15 +13584,20 @@ packages: resolution: {integrity: sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==} engines: {node: ^20.9.0 || >=22} - react-doctor@0.7.8: - resolution: {integrity: sha512-G3spmtZJE/gWWPRJ3rpgUWTPRDJpEmdRja7iNZ7RAXlfpEO+NWVzPTca/cPI9hLwPo2Aq5/BZggo5JDBrwGrlA==} + react-doctor@0.8.3: + resolution: {integrity: sha512-FfG7YQKb1yv1UNk2gknZzLAPx6L3RMOhYsYbslr4MSpVMNk5xBHlu9VxjtS0br0DEBWjkZovrf/C1MrchiIzAw==} engines: {node: ^20.19.0 || >=22.13.0} hasBin: true + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: 19.1.0 + react-dom@19.2.6: resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -13609,13 +13606,13 @@ packages: resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==} engines: {node: '>=10'} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-grab@0.1.48: resolution: {integrity: sha512-p3WnmK9LLvXE/c4ITPLlXcP1fkXo2VFEQqK94tIfcHIWKNdqdhYYFyNVioO50HR+uyHIwT63Z4txZDqJlVcD/Q==} hasBin: true peerDependencies: - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: react: optional: true @@ -13623,8 +13620,8 @@ packages: react-hotkeys-hook@4.6.2: resolution: {integrity: sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -13642,13 +13639,13 @@ packages: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 react-native-css-interop@0.2.1: resolution: {integrity: sha512-B88f5rIymJXmy1sNC/MhTkb3xxBej1KkuAt7TiT9iM7oXz3RM8Bn+7GUrfR02TvSgKm4cg2XiSuLEKYfKwNsjA==} engines: {node: '>=18'} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-reanimated: '>=3.6.2' react-native-safe-area-context: '*' @@ -13663,13 +13660,13 @@ packages: react-native-is-edge-to-edge@1.2.1: resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-keyboard-controller@1.18.5: resolution: {integrity: sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-reanimated: '>=3.0.0' @@ -13677,20 +13674,20 @@ packages: resolution: {integrity: sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ==} peerDependencies: '@babel/core': ^7.0.0-0 - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-worklets: '>=0.5.0' react-native-safe-area-context@5.6.2: resolution: {integrity: sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-screens@4.16.0: resolution: {integrity: sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-svg-transformer@1.5.3: @@ -13702,26 +13699,26 @@ packages: react-native-svg@15.15.2: resolution: {integrity: sha512-lpaSwA2i+eLvcEdDZyGgMEInQW99K06zjJqfMFblE0yxI0SCN5E4x6in46f0IYi6i3w2t2aaq3oOnyYBe+bo4w==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-web@0.21.2: resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-native-webview@13.16.0: resolution: {integrity: sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==} peerDependencies: - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native-worklets@0.7.2: resolution: {integrity: sha512-DuLu1kMV/Uyl9pQHp3hehAlThoLw7Yk2FwRTpzASOmI+cd4845FWn3m2bk9MnjUw8FBRIyhwLqYm2AJaXDXsog==} peerDependencies: '@babel/core': '*' - react: 19.2.6 + react: 19.1.0 react-native: '*' react-native@0.81.5: @@ -13730,7 +13727,7 @@ packages: hasBin: true peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -13752,7 +13749,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -13762,7 +13759,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -13770,29 +13767,22 @@ packages: react-resizable-panels@3.0.6: resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 - - react-zoom-pan-pinch@4.0.3: - resolution: {integrity: sha512-N2Hi6L78fFmhRra+ORpFSW7WST5x6kxpOPplIvtB0b7b+U2anpo1z1wLgaWRPS2kUSqcraRG+JgBCIlDJnqqAg==} - engines: {node: '>=8', npm: '>=5'} - peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-resizable-panels@4.10.0: resolution: {integrity: sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-scan@0.5.7: resolution: {integrity: sha512-KRlq734yN6q/f2CZmZi9CWHuiqSzoLhPFLtcJOL6XM4lR54myyFcY81pG9QOwj+eBC1hIHm5n+Ntbtqiilu8Rg==} hasBin: true peerDependencies: esbuild: '>=0.18.0' - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 peerDependenciesMeta: esbuild: optional: true @@ -13801,23 +13791,34 @@ packages: resolution: {integrity: sha512-kY+w4OMNZ8Nj9YI9eiTgvvJ/wYO7XyX1D/LYhvwQZv5vw69iCiDtGB0BX/2U8gLUuZAMN+x/7rHJKqHh8wXFHQ==} peerDependencies: prop-types: ^15.0.0 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true - react-test-renderer@19.2.6: - resolution: {integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==} + react-test-renderer@19.1.0: + resolution: {integrity: sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==} peerDependencies: - react: 19.2.6 + react: 19.1.0 + + react-zoom-pan-pinch@4.0.3: + resolution: {integrity: sha512-N2Hi6L78fFmhRra+ORpFSW7WST5x6kxpOPplIvtB0b7b+U2anpo1z1wLgaWRPS2kUSqcraRG+JgBCIlDJnqqAg==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: 19.1.0 + react-dom: 19.1.0 + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} react@19.2.6: resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} @@ -15146,7 +15147,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -15154,14 +15155,14 @@ packages: use-latest-callback@0.2.6: resolution: {integrity: sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==} peerDependencies: - react: 19.2.6 + react: 19.1.0 use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -15169,7 +15170,7 @@ packages: use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: - react: 19.2.6 + react: 19.1.0 utf8-byte-length@1.0.5: resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} @@ -15224,8 +15225,8 @@ packages: vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -15239,8 +15240,8 @@ packages: virtua@0.48.6: resolution: {integrity: sha512-Cl4uMvMV5c9RuOy9zhkFMYwx/V4YLBMYLRSWkO8J46opQZ3P7KMq0CqCVOOAKUckjl/r//D2jWTBGYWzmgtzrQ==} peerDependencies: - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 solid-js: '>=1.0' svelte: '>=5.0' vue: '>=3.2' @@ -15747,7 +15748,7 @@ packages: peerDependencies: '@types/react': ^19.2.15 immer: '>=9.0.6' - react: 19.2.6 + react: 19.1.0 peerDependenciesMeta: '@types/react': optional: true @@ -16838,27 +16839,27 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.6(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@base-ui/utils': 0.2.6(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 - '@base-ui/utils@0.2.6(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@base-ui/utils@0.2.6(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.29.2 '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 @@ -17151,13 +17152,13 @@ snapshots: '@dnd-kit/state': 0.1.21 tslib: 2.8.1 - '@dnd-kit/react@0.1.21(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@dnd-kit/react@0.1.21(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@dnd-kit/abstract': 0.1.21 '@dnd-kit/dom': 0.1.21 '@dnd-kit/state': 0.1.21 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) tslib: 2.8.1 '@dnd-kit/state@0.1.21': @@ -17758,7 +17759,7 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@expo/cli@54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))': + '@expo/cli@54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))': dependencies: '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) '@expo/code-signing-certificates': 0.0.6 @@ -17792,11 +17793,11 @@ snapshots: connect: 3.7.0 debug: 4.4.3 env-editor: 0.4.2 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-server: 1.0.5 freeport-async: 2.0.0 getenv: 2.0.0 - glob: 13.0.1 + glob: 13.0.6 lan-network: 0.1.7 minimatch: 9.0.5 node-forge: 1.3.3 @@ -17825,8 +17826,8 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.19.0 optionalDependencies: - expo-router: 6.0.23(d7377593e8774c4353274c7599e842cf) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo-router: 6.0.23(76047f2336d892e43bef2ac48cb56303) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - bufferutil - graphql @@ -17846,7 +17847,7 @@ snapshots: chalk: 4.1.2 debug: 4.4.3 getenv: 2.0.0 - glob: 13.0.1 + glob: 13.0.6 resolve-from: 5.0.0 semver: 7.8.4 slash: 3.0.0 @@ -17866,7 +17867,7 @@ snapshots: '@expo/json-file': 10.0.8 deepmerge: 4.3.1 getenv: 2.0.0 - glob: 13.0.1 + glob: 13.0.6 require-from-string: 2.0.2 resolve-from: 5.0.0 resolve-workspace-root: 2.0.1 @@ -17883,12 +17884,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@expo/devtools@0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: chalk: 4.1.2 optionalDependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) '@expo/env@2.0.8': dependencies: @@ -17907,7 +17908,7 @@ snapshots: chalk: 4.1.2 debug: 4.4.3 getenv: 2.0.0 - glob: 13.0.1 + glob: 13.0.6 ignore: 5.3.2 minimatch: 9.0.5 p-limit: 3.1.0 @@ -17950,7 +17951,7 @@ snapshots: dotenv: 16.4.7 dotenv-expand: 11.0.7 getenv: 2.0.0 - glob: 13.0.1 + glob: 13.0.6 hermes-parser: 0.29.1 jsc-safe-url: 0.2.4 lightningcss: 1.31.1 @@ -17958,23 +17959,23 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/metro-runtime@6.1.2(expo@54.0.33)(react-dom@19.2.6(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@expo/metro-runtime@6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: anser: 1.4.10 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) pretty-format: 29.7.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 optionalDependencies: - react-dom: 19.2.6(react@19.2.6) + react-dom: 19.1.0(react@19.1.0) '@expo/metro@54.2.0': dependencies: @@ -18026,7 +18027,7 @@ snapshots: '@expo/json-file': 10.0.8 '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) resolve-from: 5.0.0 semver: 7.8.4 xml2js: 0.6.0 @@ -18043,18 +18044,18 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/ui@0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@expo/ui@0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) sf-symbols-typescript: 2.2.0 - '@expo/vector-icons@15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@expo/vector-icons@15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) '@expo/ws-tunnel@1.0.6': {} @@ -18084,18 +18085,18 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@floating-ui/react-dom@2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@floating-ui/react@0.27.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@floating-ui/react@0.27.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@floating-ui/utils': 0.2.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) tabbable: 6.4.0 '@floating-ui/utils@0.2.11': {} @@ -18815,10 +18816,10 @@ snapshots: dependencies: zod: 4.4.3 - '@json-render/react@0.19.0(react@19.2.6)(zod@4.4.3)': + '@json-render/react@0.19.0(react@19.1.0)(zod@4.4.3)': dependencies: '@json-render/core': 0.19.0(zod@4.4.3) - react: 19.2.6 + react: 19.1.0 transitivePeerDependencies: - zod @@ -19109,11 +19110,11 @@ snapshots: '@mariozechner/clipboard-win32-x64-msvc': 0.3.9 optional: true - '@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.6)': + '@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: '@types/mdx': 2.0.13 '@types/react': 19.2.17 - react: 19.2.6 + react: 19.1.0 '@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)': dependencies: @@ -19129,6 +19130,14 @@ snapshots: '@mixmark-io/domino@2.2.0': {} + '@modelcontextprotocol/ext-apps@1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3)': + dependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + zod: 4.4.3 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@modelcontextprotocol/ext-apps@1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)': dependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) @@ -19995,10 +20004,24 @@ snapshots: tslib: 2.8.1 webcrypto-core: 1.9.2 - '@phosphor-icons/react@2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@phosphor-icons/react@2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@pierre/diffs@1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@pierre/theme': 1.0.3 + '@pierre/theming': 0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(shiki@3.23.0) + '@shikijs/transformers': 3.23.0 + diff: 8.0.3 + hast-util-to-html: 9.0.5 + lru_map: 0.4.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + shiki: 3.23.0 + transitivePeerDependencies: + - '@shikijs/themes' '@pierre/diffs@1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -20016,6 +20039,14 @@ snapshots: '@pierre/theme@1.0.3': {} + '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(shiki@3.23.0)': + optionalDependencies: + '@pierre/theme': 1.0.3 + '@shikijs/themes': 3.23.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + shiki: 3.23.0 + '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(shiki@3.23.0)': optionalDependencies: '@pierre/theme': 1.0.3 @@ -20065,15 +20096,15 @@ snapshots: dependencies: '@posthog/types': 1.386.4 - '@posthog/hedgehog-mode@0.0.53(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@posthog/hedgehog-mode@0.0.53(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: gsap: 3.14.2 lodash: 4.17.23 matter-js: 0.20.0 pixi.js: 8.16.0 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-shadow: 20.6.0(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-shadow: 20.6.0(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) uuid: 12.0.0 transitivePeerDependencies: - prop-types @@ -20082,39 +20113,39 @@ snapshots: dependencies: cross-spawn: 7.0.6 - '@posthog/quill-charts@0.3.0-beta.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@posthog/quill-charts@0.3.0-beta.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@floating-ui/react': 0.27.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@floating-ui/react': 0.27.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0) d3-array: 3.2.4 d3-color: 3.1.0 d3-scale: 4.0.2 d3-shape: 3.2.0 dayjs: 1.11.11 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) simple-statistics: 7.8.9 - '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2)': + '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.2.2)': dependencies: - '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) class-variance-authority: 0.7.1 clsx: 2.1.1 - lucide-react: 0.577.0(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-resizable-panels: 4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + lucide-react: 0.577.0(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-resizable-panels: 4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwind-merge: 2.6.1 tailwindcss: 4.2.2 - '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1)': + '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.3.1)': dependencies: - '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) class-variance-authority: 0.7.1 clsx: 2.1.1 - lucide-react: 0.577.0(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-resizable-panels: 4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + lucide-react: 0.577.0(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-resizable-panels: 4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tailwind-merge: 2.6.1 tailwindcss: 4.3.1 @@ -20166,784 +20197,784 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-context@1.1.2(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-icons@1.3.2(react@19.2.6)': + '@radix-ui/react-icons@1.3.2(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 - '@radix-ui/react-id@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) '@radix-ui/rect': 1.1.1 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) aria-hidden: 1.2.6 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.2.0(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-slot@1.2.0(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.1.0 + use-sync-external-store: 1.6.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: '@radix-ui/rect': 1.1.1 - react: 19.2.6 + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.17)(react@19.2.6)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.17)(react@19.1.0)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - react: 19.2.6 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + react: 19.1.0 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) '@radix-ui/rect@1.1.1': {} - '@radix-ui/themes@3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@radix-ui/themes@3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/colors': 3.0.0 classnames: 2.5.1 - radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.6) + radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -20959,15 +20990,15 @@ snapshots: prompts: 2.4.2 tinyexec: 1.2.4 - '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))': + '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))': dependencies: merge-options: 3.0.4 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - '@react-native-community/netinfo@12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-native-community/netinfo@12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) '@react-native/assets-registry@0.81.5': {} @@ -21081,73 +21112,73 @@ snapshots: '@react-native/normalize-colors@0.81.5': {} - '@react-native/virtualized-lists@0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-native/virtualized-lists@0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 - '@react-navigation/bottom-tabs@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-navigation/bottom-tabs@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) sf-symbols-typescript: 2.2.0 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/core@7.14.0(react@19.2.6)': + '@react-navigation/core@7.14.0(react@19.1.0)': dependencies: '@react-navigation/routers': 7.5.3 escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 nanoid: 3.3.11 query-string: 7.1.3 - react: 19.2.6 + react: 19.1.0 react-is: 19.2.6 - use-latest-callback: 0.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) + use-latest-callback: 0.2.6(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/elements@2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-navigation/elements@2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - use-latest-callback: 0.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + use-latest-callback: 0.2.6(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/native-stack@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-navigation/native-stack@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) color: 4.2.3 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + '@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': dependencies: - '@react-navigation/core': 7.14.0(react@19.2.6) + '@react-navigation/core': 7.14.0(react@19.1.0) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 nanoid: 3.3.11 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - use-latest-callback: 0.2.6(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + use-latest-callback: 0.2.6(react@19.1.0) '@react-navigation/routers@7.5.3': dependencies: @@ -21476,21 +21507,21 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-a11y@10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@storybook/addon-a11y@10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.1 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.2.6) - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.1.0) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + '@storybook/icons': 2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 optionalDependencies: '@types/react': 19.2.17 @@ -21501,10 +21532,10 @@ snapshots: - vite - webpack - '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) ts-dedent: 2.2.0 vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: @@ -21512,9 +21543,9 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.2 @@ -21524,33 +21555,33 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/icons@2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@storybook/icons@2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@5.9.3) '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3) + '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 - react: 19.2.6 + react: 19.1.0 react-docgen: 8.0.2 - react-dom: 19.2.6(react@19.2.6) + react-dom: 19.1.0(react@19.1.0) resolve: 1.22.11 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) tsconfig-paths: 4.2.0 vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: @@ -21562,15 +21593,15 @@ snapshots: - typescript - webpack - '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)': + '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) - react: 19.2.6 + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + react: 19.1.0 react-docgen: 8.0.2 react-docgen-typescript: 2.4.0(typescript@5.9.3) - react-dom: 19.2.6(react@19.2.6) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react-dom: 19.1.0(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -21578,7 +21609,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/test-runner@0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': + '@storybook/test-runner@0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 @@ -21600,7 +21631,7 @@ snapshots: playwright: 1.60.0 playwright-core: 1.60.0 rimraf: 3.0.2 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) uuid: 8.3.2 transitivePeerDependencies: - '@swc/helpers' @@ -21933,48 +21964,48 @@ snapshots: '@tanstack/query-core@5.90.20': {} - '@tanstack/react-query@5.101.0(react@19.2.6)': + '@tanstack/react-query@5.101.0(react@19.1.0)': dependencies: '@tanstack/query-core': 5.101.0 - react: 19.2.6 + react: 19.1.0 - '@tanstack/react-query@5.90.20(react@19.2.6)': + '@tanstack/react-query@5.90.20(react@19.1.0)': dependencies: '@tanstack/query-core': 5.90.20 - react: 19.2.6 + react: 19.1.0 - '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.171.13)(csstype@3.2.3) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@tanstack/router-core': 1.171.13 transitivePeerDependencies: - csstype - '@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/history': 1.162.0 - '@tanstack/react-store': 0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@tanstack/react-store': 0.9.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@tanstack/router-core': 1.171.13 isbot: 5.1.40 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - '@tanstack/react-store@0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-store@0.9.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/store': 0.9.3 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.1.0) - '@tanstack/react-virtual@3.14.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tanstack/react-virtual@3.14.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tanstack/virtual-core': 3.17.0 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) '@tanstack/router-core@1.171.13': dependencies: @@ -22004,7 +22035,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: '@babel/core': 7.29.0 '@babel/template': 7.28.6 @@ -22016,13 +22047,13 @@ snapshots: unplugin: 3.0.0 zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) webpack: 5.105.0(@swc/core@1.15.43)(esbuild@0.27.2) transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2))': + '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2))': dependencies: '@babel/core': 7.29.0 '@babel/template': 7.28.6 @@ -22034,7 +22065,7 @@ snapshots: unplugin: 3.0.0 zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) webpack: 5.105.0(esbuild@0.27.2) transitivePeerDependencies: @@ -22047,7 +22078,7 @@ snapshots: '@babel/types': 7.29.7 ansis: 4.3.1 babel-dead-code-elimination: 1.0.12 - diff: 8.0.3 + diff: 8.0.4 pathe: 2.0.3 tinyglobby: 0.2.15 transitivePeerDependencies: @@ -22079,24 +22110,24 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react-native@13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6)': + '@testing-library/react-native@13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.4.1 picocolors: 1.1.1 pretty-format: 30.4.1 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-test-renderer: 19.2.6(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: jest: 30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)) - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -22248,7 +22279,7 @@ snapshots: prosemirror-transform: 1.11.0 prosemirror-view: 1.41.5 - '@tiptap/react@3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@tiptap/react@3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@tiptap/core': 3.19.0(@tiptap/pm@3.19.0) '@tiptap/pm': 3.19.0 @@ -22256,9 +22287,9 @@ snapshots: '@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/use-sync-external-store': 0.0.6 fast-equals: 5.4.0 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - use-sync-external-store: 1.6.0(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.1.0) optionalDependencies: '@tiptap/extension-bubble-menu': 3.19.0(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) '@tiptap/extension-floating-menu': 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) @@ -22324,12 +22355,12 @@ snapshots: dependencies: typescript: 5.9.3 - '@trpc/tanstack-react-query@11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3)': + '@trpc/tanstack-react-query@11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3)': dependencies: - '@tanstack/react-query': 5.101.0(react@19.2.6) + '@tanstack/react-query': 5.101.0(react@19.1.0) '@trpc/client': 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) '@trpc/server': 11.17.0(typescript@5.9.3) - react: 19.2.6 + react: 19.1.0 typescript: 5.9.3 '@ts-morph/common@0.27.0': @@ -23460,7 +23491,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.28.6 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - supports-color @@ -23523,13 +23554,13 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 - bippy@0.5.42(react@19.2.6): + bippy@0.5.42(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 - bippy@0.5.43(react@19.2.6): + bippy@0.5.43(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 bl@4.1.0: dependencies: @@ -23579,10 +23610,6 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -23858,14 +23885,14 @@ snapshots: clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -24231,7 +24258,7 @@ snapshots: dequal@2.0.3: {} - deslop-js@0.7.8: + deslop-js@0.8.3: dependencies: '@oxc-project/types': 0.138.0 fast-glob: 3.3.3 @@ -24900,74 +24927,74 @@ snapshots: expo-application@7.0.8(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-asset@12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-asset@12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: '@expo/image-utils': 0.8.8 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - supports-color - expo-auth-session@7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-auth-session@7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: expo-application: 7.0.8(expo@54.0.33) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) expo-crypto: 15.0.8(expo@54.0.33) - expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - expo-web-browser: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-web-browser: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) invariant: 2.2.4 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - expo - supports-color - expo-av@16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-av@16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - expo-camera@55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-camera@55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: barcode-detector: 3.1.2(@types/emscripten@1.41.5) - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@types/emscripten' - expo-clipboard@55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-clipboard@55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo-constants@18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)): + expo-constants@18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): dependencies: '@expo/config': 12.0.13 '@expo/env': 2.0.8 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - supports-color expo-crypto@15.0.8(expo@54.0.33): dependencies: base64-js: 1.5.1 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-dev-client@6.0.20(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-dev-launcher: 6.0.20(expo@54.0.33) expo-dev-menu: 7.0.18(expo@54.0.33) expo-dev-menu-interface: 2.0.0(expo@54.0.33) @@ -24979,7 +25006,7 @@ snapshots: expo-dev-launcher@6.0.20(expo@54.0.33): dependencies: ajv: 8.20.0 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-dev-menu: 7.0.18(expo@54.0.33) expo-manifests: 1.0.10(expo@54.0.33) transitivePeerDependencies: @@ -24987,86 +25014,86 @@ snapshots: expo-dev-menu-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-dev-menu@7.0.18(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-dev-menu-interface: 2.0.0(expo@54.0.33) expo-device@8.0.10(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) ua-parser-js: 0.7.41 expo-document-picker@14.0.8(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)): + expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) fontfaceobserver: 2.3.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo-glass-effect@0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-glass-effect@0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) expo-haptics@55.0.14(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-image-loader@6.0.0(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-image-picker@17.0.11(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-image-loader: 6.0.0(expo@54.0.33) expo-json-utils@0.15.0: {} - expo-keep-awake@15.0.8(expo@54.0.33)(react@19.2.6): + expo-keep-awake@15.0.8(expo@54.0.33)(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 - expo-linear-gradient@15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-linear-gradient@15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo-linking@8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-linking@8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) invariant: 2.2.4 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - expo - supports-color - expo-localization@17.0.8(expo@54.0.33)(react@19.2.6): + expo-localization@17.0.8(expo@54.0.33)(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 rtl-detect: 1.1.2 expo-manifests@1.0.10(expo@54.0.33): dependencies: '@expo/config': 12.0.13 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-json-utils: 0.15.0 transitivePeerDependencies: - supports-color @@ -25079,64 +25106,64 @@ snapshots: require-from-string: 2.0.2 resolve-from: 5.0.0 - expo-modules-core@3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-modules-core@3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: invariant: 2.2.4 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo-notifications@0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-notifications@0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: '@expo/image-utils': 0.8.8 '@ide/backoff': 1.0.0 abort-controller: 3.0.0 assert: 2.1.0 badgin: 1.2.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-application: 7.0.8(expo@54.0.33) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) transitivePeerDependencies: - supports-color - expo-router@6.0.23(d7377593e8774c4353274c7599e842cf): + expo-router@6.0.23(76047f2336d892e43bef2ac48cb56303): dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.2.6(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) '@expo/schema-utils': 0.1.8 - '@radix-ui/react-slot': 1.2.0(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@react-navigation/bottom-tabs': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - '@react-navigation/native-stack': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.0(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@react-navigation/bottom-tabs': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native-stack': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) client-only: 0.0.1 debug: 4.4.3 escape-string-regexp: 4.0.0 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-server: 1.0.5 fast-deep-equal: 3.1.3 invariant: 2.2.4 nanoid: 3.3.11 query-string: 7.1.3 - react: 19.2.6 + react: 19.1.0 react-fast-compare: 3.2.2 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) semver: 7.6.3 server-only: 0.0.1 sf-symbols-typescript: 2.2.0 shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.2.6) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + use-latest-callback: 0.2.6(react@19.1.0) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: - '@testing-library/react-native': 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6) - react-dom: 19.2.6(react@19.2.6) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@testing-library/react-native': 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + react-dom: 19.1.0(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@types/react' @@ -25145,77 +25172,77 @@ snapshots: expo-secure-store@15.0.8(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-server@1.0.5: {} - expo-speech-recognition@3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-speech-recognition@3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) expo-splash-screen@31.0.13(expo@54.0.33): dependencies: '@expo/prebuild-config': 54.0.8(expo@54.0.33) - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - expo-status-bar@3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo-status-bar@3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-system-ui@6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)): + expo-system-ui@6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): dependencies: '@react-native/normalize-colors': 0.81.5 debug: 4.4.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color expo-updates-interface@2.0.0(expo@54.0.33): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-web-browser@15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)): + expo-web-browser@15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - expo@54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + expo@54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.28.6 - '@expo/cli': 54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) + '@expo/cli': 54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) '@expo/config': 12.0.13 '@expo/config-plugins': 54.0.4 - '@expo/devtools': 0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/devtools': 0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) '@expo/fingerprint': 0.15.4 '@expo/metro': 54.2.0 '@expo/metro-config': 54.0.14(expo@54.0.33) - '@expo/vector-icons': 15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/vector-icons': 15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) '@ungap/structured-clone': 1.3.0 babel-preset-expo: 54.0.10(@babel/core@7.29.0)(@babel/runtime@7.28.6)(expo@54.0.33)(react-refresh@0.14.2) - expo-asset: 12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - expo-keep-awake: 15.0.8(expo@54.0.33)(react@19.2.6) + expo-asset: 12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-keep-awake: 15.0.8(expo@54.0.33)(react@19.1.0) expo-modules-autolinking: 3.0.24 - expo-modules-core: 3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-modules-core: 3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) pretty-format: 29.7.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) react-refresh: 0.14.2 whatwg-url-without-unicode: 8.0.0-3 optionalDependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.2.6(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-webview: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-webview: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -25465,14 +25492,14 @@ snapshots: forwarded@0.2.0: {} - framer-motion@12.31.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + framer-motion@12.31.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: motion-dom: 12.30.1 motion-utils: 12.29.2 tslib: 2.8.1 optionalDependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) freeport-async@2.0.0: {} @@ -25646,12 +25673,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@13.0.1: - dependencies: - minimatch: 10.2.5 - minipass: 7.1.2 - path-scurry: 2.0.1 - glob@13.0.6: dependencies: minimatch: 10.2.5 @@ -27289,13 +27310,13 @@ snapshots: lru_map@0.4.1: {} - lucide-react@0.577.0(react@19.2.6): + lucide-react@0.577.0(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 - lucide-react@1.7.0(react@19.2.6): + lucide-react@1.7.0(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 lz-string@1.5.0: {} @@ -27962,7 +27983,7 @@ snapshots: minimatch@9.0.5: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.1.0 minimist@1.2.8: {} @@ -28127,11 +28148,11 @@ snapshots: napi-postinstall@0.3.4: {} - nativewind@4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): + nativewind@4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): dependencies: comment-json: 4.5.1 debug: 4.4.3 - react-native-css-interop: 0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) + react-native-css-interop: 0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) tailwindcss: 3.4.19(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - react @@ -28608,7 +28629,7 @@ snapshots: '@oxfmt/binding-win32-ia32-msvc': 0.45.0 '@oxfmt/binding-win32-x64-msvc': 0.45.0 - oxlint-plugin-react-doctor@0.7.8: + oxlint-plugin-react-doctor@0.8.3: dependencies: '@typescript-eslint/types': 8.62.0 eslint-scope: 9.1.2 @@ -28738,13 +28759,13 @@ snapshots: partial-json@0.1.7: {} - pastable@2.2.1(react@19.2.6): + pastable@2.2.1(react@19.1.0): dependencies: '@babel/core': 7.29.0 ts-toolbelt: 9.6.0 type-fest: 3.13.1 optionalDependencies: - react: 19.2.6 + react: 19.1.0 transitivePeerDependencies: - supports-color @@ -28767,11 +28788,6 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 - path-scurry@2.0.1: - dependencies: - lru-cache: 11.2.5 - minipass: 7.1.3 - path-scurry@2.0.2: dependencies: lru-cache: 11.2.5 @@ -28789,11 +28805,11 @@ snapshots: pe-library@0.4.1: {} - phosphor-react-native@3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + phosphor-react-native@3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) picocolors@1.1.1: {} @@ -28961,24 +28977,24 @@ snapshots: optionalDependencies: rxjs: 7.8.2 - posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - posthog-react-native@4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)))(expo-localization@17.0.8(expo@54.0.33)(react@19.2.6))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)): + posthog-react-native@4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(expo-localization@17.0.8(expo@54.0.33)(react@19.1.0))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)): dependencies: '@posthog/core': 1.20.0 - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) optionalDependencies: - '@react-native-async-storage/async-storage': 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@react-native-async-storage/async-storage': 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) expo-application: 7.0.8(expo@54.0.33) expo-device: 8.0.10(expo@54.0.33) - expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6)) - expo-localization: 17.0.8(expo@54.0.33)(react@19.2.6) - posthog-react-native-session-replay: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-localization: 17.0.8(expo@54.0.33)(react@19.1.0) + posthog-react-native-session-replay: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) postject@1.0.0-alpha.6: dependencies: @@ -29250,65 +29266,65 @@ snapshots: radix-themes-tw@0.2.3: {} - radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -29361,19 +29377,19 @@ snapshots: transitivePeerDependencies: - supports-color - react-doctor@0.7.8(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)): + react-doctor@0.8.3(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)): dependencies: '@babel/code-frame': 7.29.0 '@sentry/node': 10.61.0(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1)) agent-install: 0.0.5 conf: 15.1.0 confbox: 0.2.4 - deslop-js: 0.7.8 + deslop-js: 0.8.3 eslint-plugin-react-hooks: 7.1.1(eslint@10.5.0(jiti@2.7.0)) jiti: 2.7.0 magicast: 0.5.3 oxlint: 1.66.0 - oxlint-plugin-react-doctor: 0.7.8 + oxlint-plugin-react-doctor: 0.8.3 prompts: 2.4.2 typescript: 5.9.3 vscode-languageserver: 9.0.1 @@ -29387,6 +29403,11 @@ snapshots: - oxlint-tsgolint - supports-color + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + react-dom@19.2.6(react@19.2.6): dependencies: react: 19.2.6 @@ -29394,21 +29415,21 @@ snapshots: react-fast-compare@3.2.2: {} - react-freeze@1.0.4(react@19.2.6): + react-freeze@1.0.4(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 - react-grab@0.1.48(react@19.2.6): + react-grab@0.1.48(react@19.1.0): dependencies: '@react-grab/cli': 0.1.48 - bippy: 0.5.43(react@19.2.6) + bippy: 0.5.43(react@19.1.0) optionalDependencies: - react: 19.2.6 + react: 19.1.0 - react-hotkeys-hook@4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-hotkeys-hook@4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) react-is@16.13.1: {} @@ -29418,7 +29439,7 @@ snapshots: react-is@19.2.6: {} - react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.6): + react-markdown@10.1.0(@types/react@19.2.17)(react@19.1.0): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -29427,7 +29448,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 - react: 19.2.6 + react: 19.1.0 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -29436,79 +29457,79 @@ snapshots: transitivePeerDependencies: - supports-color - react-native-css-interop@0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): + react-native-css-interop@0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@babel/helper-module-imports': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.7 debug: 4.4.3 lightningcss: 1.27.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) semver: 7.8.4 tailwindcss: 3.4.19(tsx@4.22.4)(yaml@2.9.0) optionalDependencies: - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: '@babel/core': 7.29.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) - react-native-worklets: 0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-worklets: 0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) semver: 7.7.2 - react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-freeze: 1.0.4(react@19.2.6) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.1.0 + react-freeze: 1.0.4(react@19.1.0) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) warn-once: 0.1.1 - react-native-svg-transformer@1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(typescript@5.9.3): + react-native-svg-transformer@1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(typescript@5.9.3): dependencies: '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) path-dirname: 1.0.2 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - typescript - react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: css-select: 5.2.2 css-tree: 1.1.3 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) warn-once: 0.1.1 - react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@babel/runtime': 7.28.6 '@react-native/normalize-colors': 0.74.89 @@ -29517,20 +29538,20 @@ snapshots: memoize-one: 6.0.0 nullthrows: 1.1.1 postcss-value-parser: 4.2.0 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) styleq: 0.1.3 transitivePeerDependencies: - encoding - react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) @@ -29543,13 +29564,13 @@ snapshots: '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) '@babel/preset-typescript': 7.27.1(@babel/core@7.29.0) convert-source-map: 2.0.0 - react: 19.2.6 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) semver: 7.7.3 transitivePeerDependencies: - supports-color - react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6): + react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.81.5 @@ -29558,7 +29579,7 @@ snapshots: '@react-native/gradle-plugin': 0.81.5 '@react-native/js-polyfills': 0.81.5 '@react-native/normalize-colors': 0.81.5 - '@react-native/virtualized-lists': 0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@react-native/virtualized-lists': 0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -29576,7 +29597,7 @@ snapshots: nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 - react: 19.2.6 + react: 19.1.0 react-devtools-core: 6.1.5 react-refresh: 0.14.2 regenerator-runtime: 0.13.11 @@ -29602,55 +29623,50 @@ snapshots: react-refresh@0.18.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.6): + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.1.0): dependencies: - react: 19.2.6 - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.1.0) tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.6): + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.1.0): dependencies: - react: 19.2.6 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.6) - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.6) + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.1.0) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.6) - use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.6) + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.1.0) optionalDependencies: '@types/react': 19.2.17 - react-resizable-panels@3.0.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-resizable-panels@3.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - react-zoom-pan-pinch@4.0.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-resizable-panels@4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - react-resizable-panels@4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): - dependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) - - react-scan@0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.57.1): + react-scan@0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.57.1): dependencies: '@babel/core': 7.29.0 '@babel/types': 7.29.7 '@preact/signals': 2.9.2(preact@10.29.2) '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - bippy: 0.5.42(react@19.2.6) + bippy: 0.5.42(react@19.1.0) commander: 14.0.3 picocolors: 1.1.1 preact: 10.29.2 prompts: 2.4.2 - react: 19.2.6 - react-doctor: 0.7.8(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)) - react-dom: 19.2.6(react@19.2.6) - react-grab: 0.1.48(react@19.2.6) + react: 19.1.0 + react-doctor: 0.8.3(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)) + react-dom: 19.1.0(react@19.1.0) + react-grab: 0.1.48(react@19.1.0) optionalDependencies: esbuild: 0.27.2 unplugin: 3.0.0 @@ -29662,26 +29678,33 @@ snapshots: - rollup - supports-color - react-shadow@20.6.0(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + react-shadow@20.6.0(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: humps: 2.0.1 prop-types: 15.8.1 - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) - react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.6): + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.1.0): dependencies: get-nonce: 1.0.1 - react: 19.2.6 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - react-test-renderer@19.2.6(react@19.2.6): + react-test-renderer@19.1.0(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 react-is: 19.2.6 - scheduler: 0.27.0 + scheduler: 0.26.0 + + react-zoom-pan-pinch@4.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + react@19.1.0: {} react@19.2.6: {} @@ -30461,10 +30484,10 @@ snapshots: stdin-discarder@0.3.2: {} - storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@storybook/global': 5.0.0 - '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@storybook/icons': 2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 @@ -30476,7 +30499,7 @@ snapshots: oxc-resolver: 11.20.0 recast: 0.23.11 semver: 7.8.4 - use-sync-external-store: 1.6.0(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.1.0) ws: 8.19.0 optionalDependencies: '@types/react': 19.2.17 @@ -31088,7 +31111,7 @@ snapshots: typebox@1.1.38: {} - typed-openapi@2.2.7(openapi-types@12.1.3)(react@19.2.6): + typed-openapi@2.2.7(openapi-types@12.1.3)(react@19.1.0): dependencies: '@apidevtools/swagger-parser': 12.1.0(openapi-types@12.1.3) '@sinclair/typebox-codegen': 0.11.1 @@ -31096,7 +31119,7 @@ snapshots: cac: 7.0.0 openapi3-ts: 4.5.0 oxfmt: 0.45.0 - pastable: 2.2.1(react@19.2.6) + pastable: 2.2.1(react@19.1.0) pathe: 2.0.3 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -31258,25 +31281,29 @@ snapshots: url-join@4.0.1: {} - use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.6): + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - use-latest-callback@0.2.6(react@19.2.6): + use-latest-callback@0.2.6(react@19.1.0): dependencies: - react: 19.2.6 + react: 19.1.0 - use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.6): + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.1.0): dependencies: detect-node-es: 1.1.0 - react: 19.2.6 + react: 19.1.0 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 + use-sync-external-store@1.6.0(react@19.1.0): + dependencies: + react: 19.1.0 + use-sync-external-store@1.6.0(react@19.2.6): dependencies: react: 19.2.6 @@ -31319,11 +31346,11 @@ snapshots: vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -31343,10 +31370,10 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - virtua@0.48.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13): + virtua@0.48.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.13): optionalDependencies: - react: 19.2.6 - react-dom: 19.2.6(react@19.2.6) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) solid-js: 1.9.13 vite-tsconfig-paths@6.1.1(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@5.9.3): @@ -32024,6 +32051,14 @@ snapshots: zod@4.4.3: {} + zustand@4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0): + dependencies: + use-sync-external-store: 1.6.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.17 + immer: 11.1.3 + react: 19.1.0 + zustand@4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6): dependencies: use-sync-external-store: 1.6.0(react@19.2.6) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4b7843a910..8efd33591d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,8 +27,8 @@ catalog: '@types/react-dom': ^19.2.3 hono: ^4.6.14 inversify: ^7.10.6 - react: 19.2.6 - react-dom: 19.2.6 + react: 19.1.0 + react-dom: 19.1.0 reflect-metadata: ^0.2.2 superjson: ^2.2.2 tsup: ^8.5.1 @@ -83,11 +83,11 @@ overrides: # Dedupe the zod 4.x line to one version so cross-package schema types stay # nameable (TS2742). Scoped to 4.x; 3.x consumers are untouched. 'zod@^4.0.0': 4.4.3 - # Keep one React across the monorepo so apps/code and apps/mobile share an - # instance (react-test-renderer must match react). - react: 19.2.6 - react-dom: 19.2.6 - react-test-renderer: 19.2.6 + # Keep one React across the monorepo so shared packages resolve one runtime; + # Expo 54 and React Native 0.81 embed the React 19.1 renderer. + react: 19.1.0 + react-dom: 19.1.0 + react-test-renderer: 19.1.0 # Dedupe @types/react so shared UI Ref types unify across apps (a second copy # makes nominally-distinct Ref types and breaks ref props in packages/ui). '@types/react': ^19.2.15 From 9284b42c5848ef69fa5ed945a12d31be7a1d03fc Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Tue, 28 Jul 2026 12:01:45 +0300 Subject: [PATCH 33/42] chore(mobile): Upgrade to Expo 57 Keep the workspace on React 19.2 while aligning mobile with the latest supported native runtime. Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/code/package.json | 4 +- apps/mobile/app.json | 28 +- apps/mobile/package.json | 94 +- .../mobile/src/features/tasks/utils/sounds.ts | 22 +- apps/web/package.json | 4 +- pnpm-lock.yaml | 6050 ++++++++--------- pnpm-workspace.yaml | 13 +- 7 files changed, 2864 insertions(+), 3351 deletions(-) diff --git a/apps/code/package.json b/apps/code/package.json index b18e2e865c..16f340aab8 100644 --- a/apps/code/package.json +++ b/apps/code/package.json @@ -145,8 +145,8 @@ "node-pty": "1.1.0", "posthog-node": "^5.35.6", "radix-themes-tw": "0.2.3", - "react": "19.1.0", - "react-dom": "19.1.0", + "react": "19.2.6", + "react-dom": "19.2.6", "react-hotkeys-hook": "^4.4.4", "react-scan": "^0.5.6", "reflect-metadata": "^0.2.2", diff --git a/apps/mobile/app.json b/apps/mobile/app.json index ef66ef61ac..c22c8dc8d1 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -5,15 +5,8 @@ "version": "1.0.0", "orientation": "portrait", "userInterfaceStyle": "automatic", - "newArchEnabled": true, - "bundler": "metro", "scheme": "posthog", "icon": "./assets/app-icon.png", - "splash": { - "image": "./assets/splash-icon.png", - "resizeMode": "contain", - "backgroundColor": "#0f0f0f" - }, "ios": { "icon": "./assets/app.icon", "supportsTablet": true, @@ -33,7 +26,6 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundColor": "#FFFFFF" }, - "edgeToEdgeEnabled": true, "predictiveBackGestureEnabled": false, "package": "com.posthog.code.mobile", "permissions": [ @@ -60,12 +52,7 @@ }, "plugins": [ "expo-router", - [ - "expo-av", - { - "microphonePermission": "Allow PostHog to use your microphone for voice-to-text input" - } - ], + "expo-audio", [ "expo-camera", { @@ -120,7 +107,18 @@ "microphonePermission": "Allow PostHog to use your microphone for voice-to-text input", "speechRecognitionPermission": "Allow PostHog to transcribe your voice input on-device" } - ] + ], + "expo-secure-store", + [ + "expo-splash-screen", + { + "image": "./assets/splash-icon.png", + "resizeMode": "contain", + "backgroundColor": "#0f0f0f" + } + ], + "expo-status-bar", + "expo-web-browser" ], "extra": { "router": {}, diff --git a/apps/mobile/package.json b/apps/mobile/package.json index d49b3e47a0..5ba07d954b 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -23,8 +23,25 @@ "format": "biome format --write .", "clean": "node ../../scripts/rimraf.mjs .turbo .expo" }, + "expo": { + "doctor": { + "reactNativeDirectoryCheck": { + "exclude": [ + "posthog-react-native-session-replay" + ] + } + }, + "install": { + "exclude": [ + "react", + "react-dom", + "typescript" + ] + } + }, "dependencies": { - "@expo/ui": "0.2.0-beta.9", + "@expo/metro-runtime": "~57.0.7", + "@expo/ui": "57.0.7", "@modelcontextprotocol/ext-apps": "^1.2.2", "@modelcontextprotocol/sdk": "^1.29.0", "@posthog/api-client": "workspace:*", @@ -34,48 +51,49 @@ "@react-native-community/netinfo": "^12.0.1", "@tanstack/react-query": "^5.90.12", "date-fns": "^4.1.0", - "expo": "~54.0.27", - "expo-application": "~7.0.8", - "expo-auth-session": "^7.0.10", - "expo-av": "~16.0.8", - "expo-camera": "^55.0.15", - "expo-clipboard": "^55.0.13", - "expo-constants": "~18.0.11", - "expo-crypto": "^15.0.8", - "expo-dev-client": "~6.0.20", - "expo-device": "~8.0.10", - "expo-document-picker": "~14.0.8", - "expo-file-system": "~19.0.21", - "expo-font": "^14.0.10", - "expo-glass-effect": "~0.1.8", - "expo-haptics": "^55.0.14", - "expo-image-picker": "~17.0.11", - "expo-linear-gradient": "^15.0.8", - "expo-linking": "~8.0.10", - "expo-localization": "~17.0.8", - "expo-notifications": "~0.32.12", - "expo-router": "~6.0.17", - "expo-secure-store": "^15.0.8", - "expo-speech-recognition": "^3.1.2", - "expo-splash-screen": "~31.0.12", - "expo-status-bar": "~3.0.9", - "expo-system-ui": "~6.0.9", - "expo-web-browser": "^15.0.10", + "expo": "~57.0.8", + "expo-application": "~57.0.2", + "expo-audio": "~57.0.3", + "expo-auth-session": "^57.0.5", + "expo-camera": "^57.0.3", + "expo-clipboard": "^57.0.1", + "expo-constants": "~57.0.7", + "expo-crypto": "^57.0.1", + "expo-dev-client": "~57.0.9", + "expo-device": "~57.0.1", + "expo-document-picker": "~57.0.1", + "expo-file-system": "~57.0.1", + "expo-font": "^57.0.1", + "expo-glass-effect": "~57.0.1", + "expo-haptics": "^57.0.1", + "expo-image-picker": "~57.0.6", + "expo-linear-gradient": "^57.0.1", + "expo-linking": "~57.0.4", + "expo-localization": "~57.0.1", + "expo-notifications": "~57.0.7", + "expo-router": "~57.0.8", + "expo-secure-store": "^57.0.1", + "expo-speech-recognition": "^56.0.1", + "expo-splash-screen": "~57.0.5", + "expo-status-bar": "~57.0.1", + "expo-system-ui": "~57.0.1", + "expo-web-browser": "^57.0.2", "highlight.js": "^11.11.1", "nativewind": "^4.2.1", "phosphor-react-native": "^3.0.2", "posthog-react-native": "^4.18.0", "posthog-react-native-session-replay": "^1.6.0", - "react": "19.1.0", - "react-dom": "19.1.0", - "react-native": "0.81.5", - "react-native-keyboard-controller": "1.18.5", - "react-native-reanimated": "~4.1.1", - "react-native-safe-area-context": "~5.6.2", - "react-native-screens": "~4.16.0", - "react-native-svg": "^15.15.1", + "react": "catalog:", + "react-dom": "catalog:", + "react-native": "0.86.0", + "react-native-keyboard-controller": "1.21.9", + "react-native-reanimated": "~4.5.0", + "react-native-safe-area-context": "~5.7.0", + "react-native-screens": "~4.26.2", + "react-native-svg": "^15.15.4", "react-native-web": "^0.21.2", - "react-native-webview": "^13.13.5", + "react-native-webview": "^13.16.1", + "react-native-worklets": "0.10.0", "zustand": "^4.5.7" }, "devDependencies": { @@ -84,7 +102,7 @@ "@types/react-test-renderer": "^19.1.0", "@vitejs/plugin-react": "^4.7.0", "react-native-svg-transformer": "^1.5.3", - "react-test-renderer": "19.1.0", + "react-test-renderer": "19.2.6", "tailwindcss": "^3.4.18", "typescript": "~5.9.2", "vite": "^6.4.1", diff --git a/apps/mobile/src/features/tasks/utils/sounds.ts b/apps/mobile/src/features/tasks/utils/sounds.ts index 4ed1f10454..ebede7ffdd 100644 --- a/apps/mobile/src/features/tasks/utils/sounds.ts +++ b/apps/mobile/src/features/tasks/utils/sounds.ts @@ -1,4 +1,4 @@ -import { Audio } from "expo-av"; +import { createAudioPlayer, setAudioModeAsync } from "expo-audio"; import { type CompletionSound, usePreferencesStore, @@ -36,7 +36,7 @@ let audioModeConfigured = false; async function ensureAudioMode(): Promise { if (audioModeConfigured) return; - await Audio.setAudioModeAsync({ playsInSilentModeIOS: true }); + await setAudioModeAsync({ playsInSilentMode: true }); audioModeConfigured = true; } @@ -49,15 +49,15 @@ export async function playCompletionSound( const which = sound ?? prefs.completionSound; const vol = (volume ?? prefs.completionVolume) / 100; await ensureAudioMode(); - const { sound: player } = await Audio.Sound.createAsync(SOUND_ASSETS[which], { - shouldPlay: true, - volume: Math.max(0, Math.min(1, vol)), - rate: playbackRate, - shouldCorrectPitch: false, - }); - player.setOnPlaybackStatusUpdate((status) => { - if (status.isLoaded && status.didJustFinish) { - player.unloadAsync(); + const player = createAudioPlayer(SOUND_ASSETS[which]); + player.volume = Math.max(0, Math.min(1, vol)); + player.shouldCorrectPitch = false; + player.playbackRate = playbackRate; + const subscription = player.addListener("playbackStatusUpdate", (status) => { + if (status.didJustFinish) { + subscription.remove(); + player.remove(); } }); + player.play(); } diff --git a/apps/web/package.json b/apps/web/package.json index f710ff6c8b..bf1eff1d71 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -29,8 +29,8 @@ "@trpc/server": "^11.17.0", "@trpc/tanstack-react-query": "^11.17.0", "inversify": "^7.10.6", - "react": "19.1.0", - "react-dom": "19.1.0", + "react": "19.2.6", + "react-dom": "19.2.6", "reflect-metadata": "^0.2.2", "superjson": "catalog:", "zod": "^4.4.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffdb47c73a..a771cd1577 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,16 +83,16 @@ catalogs: version: 5.9.3 overrides: - node-abi: ^3.92.0 - zod@^4.0.0: 4.4.3 - react: 19.1.0 - react-dom: 19.1.0 - react-test-renderer: 19.1.0 + '@posthog/quill>@base-ui/react': ^1.3.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - '@posthog/quill>@base-ui/react': ^1.3.0 + node-abi: ^3.92.0 node-gyp>undici: 8.4.1 + react: 19.2.6 + react-dom: 19.2.6 + react-test-renderer: 19.2.6 vite: npm:rolldown-vite@7.3.1 + zod@^4.0.0: 4.4.3 patchedDependencies: node-pty: @@ -165,10 +165,10 @@ importers: version: 2.5.6 '@phosphor-icons/react': specifier: ^2.1.10 - version: 2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/agent': specifier: workspace:* version: link:../../packages/agent @@ -192,7 +192,7 @@ importers: version: link:../../packages/git '@posthog/hedgehog-mode': specifier: ^0.0.53 - version: 0.0.53(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.0.53(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/host-router': specifier: workspace:* version: link:../../packages/host-router @@ -204,7 +204,7 @@ importers: version: link:../../packages/platform '@posthog/quill': specifier: 'catalog:' - version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.3.1) + version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1) '@posthog/shared': specifier: workspace:* version: link:../../packages/shared @@ -219,16 +219,16 @@ importers: version: link:../../packages/workspace-server '@radix-ui/themes': specifier: ^3.2.1 - version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tailwindcss/vite': specifier: ^4.3.0 version: 4.3.1(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) '@tanstack/react-query': specifier: ^5.100.14 - version: 5.101.0(react@19.1.0) + version: 5.101.0(react@19.2.6) '@tanstack/router-plugin': specifier: ^1.168.13 - version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@trpc/client': specifier: ^11.17.0 version: 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) @@ -237,7 +237,7 @@ importers: version: 11.17.0(typescript@5.9.3) '@trpc/tanstack-react-query': specifier: ^11.17.0 - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) better-sqlite3: specifier: ^12.10.1 version: 12.10.1 @@ -299,17 +299,17 @@ importers: specifier: 0.2.3 version: 0.2.3 react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.6 + version: 19.2.6(react@19.2.6) react-hotkeys-hook: specifier: ^4.4.4 - version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-scan: specifier: ^0.5.6 - version: 0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.57.1) + version: 0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-devtools-core@6.1.5)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.57.1) reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -346,16 +346,16 @@ importers: version: 1.4.5(rollup@4.57.1) '@storybook/addon-a11y': specifier: 10.4.1 - version: 10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@storybook/addon-docs': specifier: 10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@storybook/react-vite': specifier: 10.4.1 - version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + version: 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) '@storybook/test-runner': specifier: ^0.24.4 - version: 0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) '@tanstack/devtools-vite': specifier: ^0.8.1 version: 0.8.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) @@ -364,7 +364,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) @@ -439,7 +439,7 @@ importers: version: 8.5.15 storybook: specifier: 10.4.1 - version: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tailwindcss: specifier: ^4.3.0 version: 4.3.1 @@ -448,7 +448,7 @@ importers: version: 4.22.4 typed-openapi: specifier: ^2.2.6 - version: 2.2.7(openapi-types@12.1.3)(react@19.1.0) + version: 2.2.7(openapi-types@12.1.3)(react@19.2.6) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -470,12 +470,15 @@ importers: apps/mobile: dependencies: + '@expo/metro-runtime': + specifier: ~57.0.7 + version: 57.0.7(@expo/log-box@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) '@expo/ui': - specifier: 0.2.0-beta.9 - version: 0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: 57.0.7 + version: 57.0.7(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) '@modelcontextprotocol/ext-apps': specifier: ^1.2.2 - version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3) + version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3) '@modelcontextprotocol/sdk': specifier: ^1.29.0 version: 1.29.0(zod@4.4.3) @@ -490,149 +493,152 @@ importers: version: link:../../packages/shared '@react-native-async-storage/async-storage': specifier: ^2.2.0 - version: 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + version: 2.2.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) '@react-native-community/netinfo': specifier: ^12.0.1 - version: 12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + version: 12.0.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) '@tanstack/react-query': specifier: ^5.90.12 - version: 5.90.20(react@19.1.0) + version: 5.90.20(react@19.2.6) date-fns: specifier: ^4.1.0 version: 4.1.0 expo: - specifier: ~54.0.27 - version: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~57.0.8 + version: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) expo-application: - specifier: ~7.0.8 - version: 7.0.8(expo@54.0.33) + specifier: ~57.0.2 + version: 57.0.2(expo@57.0.8) + expo-audio: + specifier: ~57.0.3 + version: 57.0.3(expo-asset@57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-auth-session: - specifier: ^7.0.10 - version: 7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-av: - specifier: ~16.0.8 - version: 16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^57.0.5 + version: 57.0.5(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-camera: - specifier: ^55.0.15 - version: 55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^57.0.3 + version: 57.0.3(@types/emscripten@1.41.5)(expo@57.0.8)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-clipboard: - specifier: ^55.0.13 - version: 55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-constants: - specifier: ~18.0.11 - version: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + specifier: ~57.0.7 + version: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) expo-crypto: - specifier: ^15.0.8 - version: 15.0.8(expo@54.0.33) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8) expo-dev-client: - specifier: ~6.0.20 - version: 6.0.20(expo@54.0.33) + specifier: ~57.0.9 + version: 57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) expo-device: - specifier: ~8.0.10 - version: 8.0.10(expo@54.0.33) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8) expo-document-picker: - specifier: ~14.0.8 - version: 14.0.8(expo@54.0.33) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8) expo-file-system: - specifier: ~19.0.21 - version: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) expo-font: - specifier: ^14.0.10 - version: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-glass-effect: - specifier: ~0.1.8 - version: 0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-haptics: - specifier: ^55.0.14 - version: 55.0.14(expo@54.0.33) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8) expo-image-picker: - specifier: ~17.0.11 - version: 17.0.11(expo@54.0.33) + specifier: ~57.0.6 + version: 57.0.6(expo@57.0.8) expo-linear-gradient: - specifier: ^15.0.8 - version: 15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-linking: - specifier: ~8.0.10 - version: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~57.0.4 + version: 57.0.4(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-localization: - specifier: ~17.0.8 - version: 17.0.8(expo@54.0.33)(react@19.1.0) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8)(react@19.2.6) expo-notifications: - specifier: ~0.32.12 - version: 0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~57.0.7 + version: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) expo-router: - specifier: ~6.0.17 - version: 6.0.23(76047f2336d892e43bef2ac48cb56303) + specifier: ~57.0.8 + version: 57.0.8(507433f30d6a233882de5eb4189dda28) expo-secure-store: - specifier: ^15.0.8 - version: 15.0.8(expo@54.0.33) + specifier: ^57.0.1 + version: 57.0.1(expo@57.0.8) expo-speech-recognition: - specifier: ^3.1.2 - version: 3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^56.0.1 + version: 56.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-splash-screen: - specifier: ~31.0.12 - version: 31.0.13(expo@54.0.33) + specifier: ~57.0.5 + version: 57.0.5(expo@57.0.8)(typescript@5.9.3) expo-status-bar: - specifier: ~3.0.9 - version: 3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) expo-system-ui: - specifier: ~6.0.9 - version: 6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + specifier: ~57.0.1 + version: 57.0.1(expo@57.0.8)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) expo-web-browser: - specifier: ^15.0.10 - version: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + specifier: ^57.0.2 + version: 57.0.2(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) highlight.js: specifier: ^11.11.1 version: 11.11.1 nativewind: specifier: ^4.2.1 - version: 4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) + version: 4.2.1(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) phosphor-react-native: specifier: ^3.0.2 - version: 3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + version: 3.0.3(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) posthog-react-native: specifier: ^4.18.0 - version: 4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(expo-localization@17.0.8(expo@54.0.33)(react@19.1.0))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)) + version: 4.30.0(09aed2ab7f1f7ef1818c91ffd64580ed) posthog-react-native-session-replay: specifier: ^1.6.0 - version: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + version: 1.6.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.6 + version: 19.2.6(react@19.2.6) react-native: - specifier: 0.81.5 - version: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + specifier: 0.86.0 + version: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) react-native-keyboard-controller: - specifier: 1.18.5 - version: 1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: 1.21.9 + version: 1.21.9(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react-native-reanimated: - specifier: ~4.1.1 - version: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~4.5.0 + version: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react-native-safe-area-context: - specifier: ~5.6.2 - version: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~5.7.0 + version: 5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react-native-screens: - specifier: ~4.16.0 - version: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ~4.26.2 + version: 4.26.2(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react-native-svg: - specifier: ^15.15.1 - version: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^15.15.4 + version: 15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) react-native-web: specifier: ^0.21.2 - version: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-native-webview: - specifier: ^13.13.5 - version: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + specifier: ^13.16.1 + version: 13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-worklets: + specifier: 0.10.0 + version: 0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) zustand: specifier: ^4.5.7 - version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0) + version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6) devDependencies: '@testing-library/react-native': specifier: ^13.3.3 - version: 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + version: 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6) '@types/react': specifier: ^19.2.15 version: 19.2.17 @@ -644,10 +650,10 @@ importers: version: 4.7.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@1.21.7)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) react-native-svg-transformer: specifier: ^1.5.3 - version: 1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(typescript@5.9.3) + version: 1.5.3(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(typescript@5.9.3) react-test-renderer: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.6 + version: 19.2.6(react@19.2.6) tailwindcss: specifier: ^3.4.18 version: 3.4.19(tsx@4.22.4)(yaml@2.9.0) @@ -671,7 +677,7 @@ importers: version: 2.2.0(inversify@7.11.0(reflect-metadata@0.2.2)) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/agent': specifier: workspace:* version: link:../../packages/agent @@ -701,7 +707,7 @@ importers: version: link:../../packages/workspace-client '@tanstack/react-query': specifier: ^5.100.14 - version: 5.101.0(react@19.1.0) + version: 5.101.0(react@19.2.6) '@trpc/client': specifier: ^11.17.0 version: 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) @@ -710,16 +716,16 @@ importers: version: 11.17.0(typescript@5.9.3) '@trpc/tanstack-react-query': specifier: ^11.17.0 - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) inversify: specifier: ^7.10.6 version: 7.11.0(reflect-metadata@0.2.2) react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.6 + version: 19.2.6(react@19.2.6) reflect-metadata: specifier: ^0.2.2 version: 0.2.2 @@ -738,7 +744,7 @@ importers: version: 4.2.2(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) '@tanstack/router-plugin': specifier: 'catalog:' - version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2)) + version: 1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2)) '@types/react': specifier: ^19.2.15 version: 19.2.17 @@ -771,13 +777,13 @@ importers: version: 0.109.0(zod@4.4.3) '@earendil-works/pi-agent-core': specifier: 'catalog:' - version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@earendil-works/pi-ai': specifier: 'catalog:' - version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@earendil-works/pi-coding-agent': specifier: 'catalog:' - version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@hono/node-server': specifier: ^1.19.9 version: 1.19.9(hono@4.11.7) @@ -966,8 +972,8 @@ importers: specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 typescript: specifier: 'catalog:' version: 5.9.3 @@ -1056,10 +1062,10 @@ importers: dependencies: '@earendil-works/pi-ai': specifier: 'catalog:' - version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@earendil-works/pi-coding-agent': specifier: 'catalog:' - version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + version: 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@earendil-works/pi-tui': specifier: 'catalog:' version: 0.80.6 @@ -1136,16 +1142,16 @@ importers: version: link:../../tooling/typescript '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.1.0) + version: 5.101.0(react@19.2.6) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) '@types/react': specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 typescript: specifier: 'catalog:' version: 5.9.3 @@ -1204,7 +1210,7 @@ importers: version: 0.22.1(zod@4.4.3) '@base-ui/react': specifier: ^1.3.0 - version: 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@codemirror/lang-angular': specifier: ^0.1.4 version: 0.1.4 @@ -1282,7 +1288,7 @@ importers: version: 0.1.21 '@dnd-kit/react': specifier: ^0.1.21 - version: 0.1.21(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.1.21(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@joplin/turndown-plugin-gfm': specifier: ^1.0.67 version: 1.0.67 @@ -1291,7 +1297,7 @@ importers: version: 0.19.0(zod@4.4.3) '@json-render/react': specifier: ^0.19.0 - version: 0.19.0(react@19.1.0)(zod@4.4.3) + version: 0.19.0(react@19.2.6)(zod@4.4.3) '@lezer/common': specifier: ^1.5.1 version: 1.5.1 @@ -1300,13 +1306,13 @@ importers: version: 1.2.3 '@modelcontextprotocol/ext-apps': specifier: ^1.1.2 - version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3) + version: 1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3) '@modelcontextprotocol/sdk': specifier: ^1.12.1 version: 1.29.0(zod@4.4.3) '@pierre/diffs': specifier: ^1.2.10 - version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/agent': specifier: workspace:* version: link:../agent @@ -1330,7 +1336,7 @@ importers: version: link:../platform '@posthog/quill-charts': specifier: 0.3.0-beta.19 - version: 0.3.0-beta.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.3.0-beta.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/shared': specifier: workspace:* version: link:../shared @@ -1339,22 +1345,22 @@ importers: version: link:../workspace-client '@radix-ui/react-collapsible': specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@radix-ui/react-icons': specifier: ^1.3.2 - version: 1.3.2(react@19.1.0) + version: 1.3.2(react@19.2.6) '@radix-ui/react-tooltip': specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-router': specifier: 'catalog:' - version: 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-router-devtools': specifier: 'catalog:' - version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-virtual': specifier: ^3.13.26 - version: 3.14.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.14.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tiptap/core': specifier: ^3.13.0 version: 3.19.0(@tiptap/pm@3.19.0) @@ -1369,7 +1375,7 @@ importers: version: 3.19.0 '@tiptap/react': specifier: ^3.13.0 - version: 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tiptap/starter-kit': specifier: ^3.13.0 version: 3.19.0 @@ -1378,7 +1384,7 @@ importers: version: 3.19.0(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) '@xterm/addon-fit': specifier: ^0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) @@ -1402,10 +1408,10 @@ importers: version: 2.1.1 cmdk: specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) framer-motion: specifier: ^12.26.2 - version: 12.31.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 12.31.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) fuse.js: specifier: ^7.1.0 version: 7.1.0 @@ -1417,7 +1423,7 @@ importers: version: 7.11.0(reflect-metadata@0.2.2) lucide-react: specifier: ^1.7.0 - version: 1.7.0(react@19.1.0) + version: 1.7.0(react@19.2.6) posthog-js: specifier: ^1.378.0 version: 1.386.8 @@ -1426,16 +1432,16 @@ importers: version: 0.2.3 react-hotkeys-hook: specifier: ^4.4.4 - version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.17)(react@19.1.0) + version: 10.1.0(@types/react@19.2.17)(react@19.2.6) react-resizable-panels: specifier: ^3.0.6 - version: 3.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.0.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6) react-zoom-pan-pinch: specifier: ^4.0.3 - version: 4.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 4.0.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) reflect-metadata: specifier: 'catalog:' version: 0.2.2 @@ -1468,7 +1474,7 @@ importers: version: 11.0.5 virtua: specifier: ^0.48.6 - version: 0.48.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.13) + version: 0.48.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13) vscode-icons-js: specifier: ^11.6.1 version: 11.6.1 @@ -1477,23 +1483,23 @@ importers: version: 4.4.3 zustand: specifier: ^4.5.0 - version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0) + version: 4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6) devDependencies: '@phosphor-icons/react': specifier: 'catalog:' - version: 2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@posthog/quill': specifier: 'catalog:' - version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.2.2) + version: 0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2) '@posthog/tsconfig': specifier: workspace:* version: link:../../tooling/typescript '@radix-ui/themes': specifier: 'catalog:' - version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.1.0) + version: 5.101.0(react@19.2.6) '@tanstack/router-generator': specifier: 'catalog:' version: 1.167.17 @@ -1502,7 +1508,7 @@ importers: version: 6.9.1 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) @@ -1528,11 +1534,11 @@ importers: specifier: ^26.0.0 version: 26.1.0 react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.2.6 + version: 19.2.6(react@19.2.6) typescript: specifier: 'catalog:' version: 5.9.3 @@ -1557,16 +1563,16 @@ importers: version: link:../workspace-server '@tanstack/react-query': specifier: 'catalog:' - version: 5.101.0(react@19.1.0) + version: 5.101.0(react@19.2.6) '@trpc/tanstack-react-query': specifier: 'catalog:' - version: 11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3) + version: 11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3) '@types/react': specifier: ^19.2.15 version: 19.2.17 react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.2.6 + version: 19.2.6 typescript: specifier: 'catalog:' version: 5.9.3 @@ -1681,14 +1687,6 @@ importers: packages: - '@0no-co/graphql.web@1.2.0': - resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - peerDependenciesMeta: - graphql: - optional: true - '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} @@ -1707,6 +1705,10 @@ packages: peerDependencies: zod: 4.4.3 + '@alcalzone/ansi-tokenize@0.3.0': + resolution: {integrity: sha512-p+CMKJ93HFmLkjXKlXiVGlMQEuRb6H0MokBSwUsX+S6BRX8eV5naFZpQJFfJHjRZY0Hmnqy1/r6UWl3x+19zYA==} + engines: {node: '>=18'} + '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -1970,9 +1972,6 @@ packages: resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} - '@babel/code-frame@7.10.4': - resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==} - '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} @@ -2046,6 +2045,10 @@ packages: resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + '@babel/helper-remap-async-to-generator@7.27.1': resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} engines: {node: '>=6.9.0'} @@ -2090,10 +2093,6 @@ packages: resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.25.9': - resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} @@ -2254,12 +2253,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.27.1': - resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.28.6': resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} engines: {node: '>=6.9.0'} @@ -2272,24 +2265,12 @@ packages: peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.4': - resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-classes@7.28.6': resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.28.6': - resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.5': resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} engines: {node: '>=6.9.0'} @@ -2314,18 +2295,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-function-name@7.27.1': - resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-literals@7.27.1': - resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.28.6': resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} engines: {node: '>=6.9.0'} @@ -2344,24 +2313,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': - resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.28.6': - resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.6': resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} engines: {node: '>=6.9.0'} @@ -2374,12 +2331,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.27.1': - resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.28.6': resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} engines: {node: '>=6.9.0'} @@ -2440,8 +2391,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.29.0': - resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} + '@babel/plugin-transform-regenerator@7.29.7': + resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2458,18 +2409,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.28.6': - resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-sticky-regex@7.27.1': - resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-template-literals@7.27.1': resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} engines: {node: '>=6.9.0'} @@ -2488,18 +2427,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-react@7.28.5': - resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-typescript@7.27.1': - resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.28.5': resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} engines: {node: '>=6.9.0'} @@ -2535,8 +2462,8 @@ packages: engines: {node: '>=14.0.0'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -2545,8 +2472,8 @@ packages: resolution: {integrity: sha512-yQ+qeuqohwhsNpoYDqqXaLllYAkPCP4vYdDrVo8FQXaAPfHWm1pG/Vm+jmGTA5JFS0BAIjookyapuJFY8F9PIw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -2739,8 +2666,8 @@ packages: '@dnd-kit/react@0.1.21': resolution: {integrity: sha512-fxcr1tWF7+KSNq464ZOGvQETSH9zYb68VOdx8Ie3XoCUnNicJW5YBZrwvMeDhUDnvLS+W2iHiVuUjtXDKJjNeg==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@dnd-kit/state@0.1.21': resolution: {integrity: sha512-pdhntEPvn/QttcF295bOJpWiLsRqA/Iczh1ODOJUxGiR+E4GkYVz9VapNNm9gDq6ST0tr/e1Q2xBztUHlJqQgA==} @@ -2823,9 +2750,6 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/core@1.11.1': - resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - '@emnapi/core@1.11.2': resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} @@ -2838,9 +2762,6 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - '@emnapi/runtime@1.11.2': resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} @@ -3497,8 +3418,11 @@ packages: resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@expo/cli@54.0.23': - resolution: {integrity: sha512-km0h72SFfQCmVycH/JtPFTVy69w6Lx1cHNDmfLfQqgKFYeeHTjx7LVDP4POHCtNxFP2UeRazrygJhlh4zz498g==} + '@expo-google-fonts/material-symbols@0.4.42': + resolution: {integrity: sha512-KZmHZRcthJ3KFZZlpzHjopA9guZgWR9fb3uVZlTR0BNlvG2pw1bnYBCpkze2PB0vRllwGhAM7lWXsfmcWCbXYg==} + + '@expo/cli@57.0.10': + resolution: {integrity: sha512-mP+B9ZrTnRE94bxPM/kCVKPyuVuOTRvvsqbXVxdXtrAfOfF94YIZLGUtw/151cGFtdUPbsBsJ9gW02LhU+QMZA==} hasBin: true peerDependencies: expo: '*' @@ -3513,22 +3437,22 @@ packages: '@expo/code-signing-certificates@0.0.6': resolution: {integrity: sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w==} - '@expo/config-plugins@54.0.4': - resolution: {integrity: sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==} + '@expo/config-plugins@57.0.6': + resolution: {integrity: sha512-7CmKrS5Rnu8aSZyNlxH2qzA7Ls1HEa4EQvEVOAkHDKPr1e4Cg/nz7I7dUl09QDTVjMvkKYDH4Th0DuAsgqASaw==} - '@expo/config-types@54.0.10': - resolution: {integrity: sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==} + '@expo/config-types@57.0.2': + resolution: {integrity: sha512-ewW08OonrcRIsRKIlFvvcmmafE5zemb1ocu3HkNwtVPyRtj2w42pZCAkMIROYpcVBaPnc3mDT9UZDzwXWC3i6g==} - '@expo/config@12.0.13': - resolution: {integrity: sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==} + '@expo/config@57.0.6': + resolution: {integrity: sha512-VpMJpB/De/fb9bBFVVBiK6Ntg9lt0kAleLH9hcZz85CYRUQ3jVFVA8rNC5f8y4cp2+FiiPNFp62+kEOFI6pDiw==} '@expo/devcert@1.2.1': resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} - '@expo/devtools@0.1.8': - resolution: {integrity: sha512-SVLxbuanDjJPgc0sy3EfXUMLb/tXzp6XIHkhtPVmTWJAp+FOr6+5SeiCfJrCzZFet0Ifyke2vX3sFcKwEvCXwQ==} + '@expo/devtools@57.0.1': + resolution: {integrity: sha512-GyUf+wFNkbttaX0jR7MZa9bm77U0IrLg6d2AjpxdyoXw/w4abHoXG0oFufwLMgP9zLTd5+Ct4X/ffNUTnlzZgg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' peerDependenciesMeta: react: @@ -3536,88 +3460,150 @@ packages: react-native: optional: true - '@expo/env@2.0.8': - resolution: {integrity: sha512-5VQD6GT8HIMRaSaB5JFtOXuvfDVU80YtZIuUT/GDhUF782usIXY13Tn3IdDz1Tm/lqA9qnRZQ1BF4t7LlvdJPA==} + '@expo/dom-webview@57.0.1': + resolution: {integrity: sha512-lAKsME4SAq+8sf56oN0DX5TBYyruupoRxbWbD2xf9RnKY8y6x8eb9LCE5pxSN0qyWdqnp+0wmyWzDkKboThKAw==} + peerDependencies: + expo: '*' + react: 19.2.6 + react-native: '*' + + '@expo/env@2.4.2': + resolution: {integrity: sha512-28pqaEqwnmLduZ00Pq9HkSzE5wbj1MTwp5/n8nm8rD8MCjR9eUnVOwmNksPI3Be2ReAPO/DbPn1puy0mvoocsQ==} + engines: {node: '>=20.12.0'} - '@expo/fingerprint@0.15.4': - resolution: {integrity: sha512-eYlxcrGdR2/j2M6pEDXo9zU9KXXF1vhP+V+Tl+lyY+bU8lnzrN6c637mz6Ye3em2ANy8hhUR03Raf8VsT9Ogng==} + '@expo/expo-modules-macros-plugin@0.6.1': + resolution: {integrity: sha512-cpsLZE4rqkc1Y3eZTkxB98jrqY1YXgetmtxFt8q89jBRmk3quRuk1BZo+VcnCSObZardjg99r1k5xijEMONFGA==} + + '@expo/fingerprint@0.20.6': + resolution: {integrity: sha512-cmC/6BOPRbdKr77Mgjwszb8aM0hY2RKBpMRCmjSdn9zIcn2FGor/ic4fHVr46cQFa1G6RDGg1GyAjRw3US4CCQ==} hasBin: true - '@expo/image-utils@0.8.8': - resolution: {integrity: sha512-HHHaG4J4nKjTtVa1GG9PCh763xlETScfEyNxxOvfTRr8IKPJckjTyqSLEtdJoFNJ1vqiABEjW7tqGhqGibZLeA==} + '@expo/image-utils@0.11.4': + resolution: {integrity: sha512-pn/4770DIEOcYZr484uazuwg20FX/qaDkeMRF6J+oxejynDmEmO8wLsCudaNShFE0BhyKGQTYrs2rsRhqrqESw==} + + '@expo/inline-modules@0.1.3': + resolution: {integrity: sha512-eHSxWYfgq65mP3Qz8PclVjUkSrDIlGl3va9U7PMcTpGItOvee/i0ZzGinH5A25oARR5ouD64eESBKwtT/CwdHg==} + + '@expo/json-file@11.0.1': + resolution: {integrity: sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==} + + '@expo/local-build-cache-provider@57.0.4': + resolution: {integrity: sha512-B/cI73shkLSYBYuFyh+zCbS+WhqJgawWPW4MPdMiNLJKv9RmV4dv1FGjsidiIiG2k4kYKerBDLK4bLbC7qERQQ==} - '@expo/json-file@10.0.8': - resolution: {integrity: sha512-9LOTh1PgKizD1VXfGQ88LtDH0lRwq9lsTb4aichWTWSWqy3Ugfkhfm3BhzBIkJJfQQ5iJu3m/BoRlEIjoCGcnQ==} + '@expo/log-box@57.0.1': + resolution: {integrity: sha512-fuVNHhOerdRWtpq27gD6JTSVYESsfRu+SMdrNCWxW+gFnusS6dGKfx3lKGBZ4ZkMNiLWn8maBHo39YKzJNXFYQ==} + peerDependencies: + '@expo/dom-webview': ^57.0.1 + expo: '*' + react: 19.2.6 + react-native: '*' - '@expo/metro-config@54.0.14': - resolution: {integrity: sha512-hxpLyDfOR4L23tJ9W1IbJJsG7k4lv2sotohBm/kTYyiG+pe1SYCAWsRmgk+H42o/wWf/HQjE5k45S5TomGLxNA==} + '@expo/metro-config@57.0.7': + resolution: {integrity: sha512-bVfEkg4zF1cA62OqAdYXmFOooJ6TB/I+REi7Se6Ct+PbSC+89TwSqWXnYx34L08eIs4z+1ilgbATakTZpgefmQ==} peerDependencies: expo: '*' peerDependenciesMeta: expo: optional: true - '@expo/metro-runtime@6.1.2': - resolution: {integrity: sha512-nvM+Qv45QH7pmYvP8JB1G8JpScrWND3KrMA6ZKe62cwwNiX/BjHU28Ear0v/4bQWXlOY0mv6B8CDIm8JxXde9g==} + '@expo/metro-file-map@57.0.1': + resolution: {integrity: sha512-8JXfVstZN7QnP4NianZZnlTVboOWR0sG8trUDNajOjnbGlPln29vponXM84tY+3tAHapz5/TxE53L0ixUwqPtA==} + + '@expo/metro-runtime@57.0.7': + resolution: {integrity: sha512-95UeoN/YsLellvskKsFGN9vKBwNc5k70ysO3skqfL3VusWlYIiYmPY+MpEWNz8A8W2CjLg9AKwZKAGrFj6znQg==} peerDependencies: + '@expo/log-box': ^57.0.1 expo: '*' - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-native: '*' peerDependenciesMeta: react-dom: optional: true - '@expo/metro@54.2.0': - resolution: {integrity: sha512-h68TNZPGsk6swMmLm9nRSnE2UXm48rWwgcbtAHVMikXvbxdS41NDHHeqg1rcQ9AbznDRp6SQVC2MVpDnsRKU1w==} + '@expo/metro@56.0.0': + resolution: {integrity: sha512-5gIgQHtEpjjvsjKfVtIv23a98LLRV0/y07PDShEwYSytAMlE3FSF8RHXqtHc1sUJL6dn7hnuIBpIbrLXXuVi0A==} - '@expo/osascript@2.3.8': - resolution: {integrity: sha512-/TuOZvSG7Nn0I8c+FcEaoHeBO07yu6vwDgk7rZVvAXoeAK5rkA09jRyjYsZo+0tMEFaToBeywA6pj50Mb3ny9w==} + '@expo/osascript@2.7.1': + resolution: {integrity: sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==} engines: {node: '>=12'} - '@expo/package-manager@1.9.10': - resolution: {integrity: sha512-axJm+NOj3jVxep49va/+L3KkF3YW/dkV+RwzqUJedZrv4LeTqOG4rhrCaCPXHTvLqCTDKu6j0Xyd28N7mnxsGA==} + '@expo/package-manager@1.13.1': + resolution: {integrity: sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==} + + '@expo/plist@0.8.1': + resolution: {integrity: sha512-3gTReGIUm0oRaMClsAJYxBnVPCl6fVpsl8HS+DTVxDhW4GyVyxg9E/Znm3BvcHtUJ51RJJI14pC1wvrNilCRHw==} + + '@expo/prebuild-config@57.0.9': + resolution: {integrity: sha512-8g7RoXFvO/dxvLzRE/bvphzDL4bfV0w3/4Aj6DfwvgymZ1ULz5gW2x0js94opZRoZvWw4SolH6/74hLlZT3rAA==} - '@expo/plist@0.4.8': - resolution: {integrity: sha512-pfNtErGGzzRwHP+5+RqswzPDKkZrx+Cli0mzjQaus1ZWFsog5ibL+nVT3NcporW51o8ggnt7x813vtRbPiyOrQ==} + '@expo/require-utils@57.0.4': + resolution: {integrity: sha512-e7xbg/9BTQcsZE/oErafZXtI7kh5IgfasLJ97J5sFSzX2cA74pDvdlhW1KHVSaDkQyQv6h1LSLhsY7dEeOk7hw==} + peerDependencies: + typescript: ^5.0.0 || ^5.0.0-0 || ^6.0.0 || ^7.0.0 + peerDependenciesMeta: + typescript: + optional: true - '@expo/prebuild-config@54.0.8': - resolution: {integrity: sha512-EA7N4dloty2t5Rde+HP0IEE+nkAQiu4A/+QGZGT9mFnZ5KKjPPkqSyYcRvP5bhQE10D+tvz6X0ngZpulbMdbsg==} + '@expo/router-server@57.0.4': + resolution: {integrity: sha512-ucqCP0hK8nZb9+S8QJYdQxNCkfRClzkKdg2RpWYDKDYgRIHyoRyxEDRgDgvbhH+q78yfY83abU8OTx8MMOrf1g==} peerDependencies: + '@expo/metro-runtime': ^57.0.7 expo: '*' + expo-constants: ^57.0.7 + expo-font: ^57.0.1 + expo-router: '*' + expo-server: ^57.0.1 + react: 19.2.6 + react-dom: 19.2.6 + react-server-dom-webpack: ~19.0.1 || ~19.1.2 || ~19.2.1 + peerDependenciesMeta: + '@expo/metro-runtime': + optional: true + expo-router: + optional: true + react-dom: + optional: true + react-server-dom-webpack: + optional: true - '@expo/schema-utils@0.1.8': - resolution: {integrity: sha512-9I6ZqvnAvKKDiO+ZF8BpQQFYWXOJvTAL5L/227RUbWG1OVZDInFifzCBiqAZ3b67NRfeAgpgvbA7rejsqhY62A==} + '@expo/schema-utils@57.0.2': + resolution: {integrity: sha512-fMu/jyN0l1Wzv7XkeWR4IYCx1M8ryui3FdBNGrWwbRgJ7EhxXxK8E2jxP2W3pbgUwUY0V3hG8+GyfCZwny+Lxw==} '@expo/sdk-runtime-versions@1.0.0': resolution: {integrity: sha512-Doz2bfiPndXYFPMRwPyGa1k5QaKDVpY806UJj570epIiMzWaYyCtobasyfC++qfIXVb5Ocy7r3tP9d62hAQ7IQ==} - '@expo/spawn-async@1.7.2': - resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==} + '@expo/spawn-async@1.8.0': + resolution: {integrity: sha512-eb9xxd/LbuEGSdua4NumCu/McVB9EM+F/JxB9pWgnERw4HQ9XyTNH1KapG6oqLWR8TuRK2LQfzJlmNi94CVobw==} engines: {node: '>=12'} '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} - '@expo/ui@0.2.0-beta.9': - resolution: {integrity: sha512-RaBcp0cMe5GykQogJwRZGy4o4JHDLtrr+HaurDPhwPKqVATsV0rR11ysmFe4QX8XWLP/L3od7NOkXUi5ailvaw==} + '@expo/ui@57.0.7': + resolution: {integrity: sha512-WqRVabl8VpHf3+YLHVjUy7PMIuXXI6DG88Vgmavro7Nd8Ks13h9sEJH/RLSCaJE2daVnqzEKY1v797BibuY9aw==} peerDependencies: + '@babel/core': '*' expo: '*' - react: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-native: '*' + react-native-worklets: '*' + peerDependenciesMeta: + '@babel/core': + optional: true + react-dom: + optional: true + react-native-worklets: + optional: true - '@expo/vector-icons@15.0.3': - resolution: {integrity: sha512-SBUyYKphmlfUBqxSfDdJ3jAdEVSALS2VUPOUyqn48oZmb2TL/O7t7/PQm5v4NQujYEPLPMTLn9KVw6H7twwbTA==} + '@expo/ws-tunnel@2.0.0': + resolution: {integrity: sha512-j+JfTRdCk820J9dU0sA2SqshQIKFOMo7ED84w9MJFcebfbNQgsLztEY/SABDkGnjatrW4xGqnUhVRxSBVyCkXw==} peerDependencies: - expo-font: '>=14.0.4' - react: 19.1.0 - react-native: '*' - - '@expo/ws-tunnel@1.0.6': - resolution: {integrity: sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q==} + ws: ^8.0.0 - '@expo/xcpretty@4.4.0': - resolution: {integrity: sha512-o2qDlTqJ606h4xR36H2zWTywmZ3v3842K6TU8Ik2n1mfW0S580VHlt3eItVYdLYz+klaPp7CXqanja8eASZjRw==} + '@expo/xcpretty@4.4.4': + resolution: {integrity: sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw==} hasBin: true '@floating-ui/core@1.7.4': @@ -3635,14 +3621,14 @@ packages: '@floating-ui/react-dom@2.1.8': resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@floating-ui/react@0.27.19': resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} @@ -3701,9 +3687,6 @@ packages: '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - '@ide/backoff@1.0.0': - resolution: {integrity: sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==} - '@inquirer/ansi@1.0.2': resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} @@ -3807,10 +3790,6 @@ packages: node-notifier: optional: true - '@jest/create-cache-key-function@29.7.0': - resolution: {integrity: sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/create-cache-key-function@30.4.1': resolution: {integrity: sha512-R+xGEtzA95NIsvpXJSROG4t01956dDOt17KpamguY4XOnGvdHNFFXE7Er0C1OAsRjOwiIxpKqOvGlznIGZIQlQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3819,10 +3798,6 @@ packages: resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/environment@30.4.1': resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3835,10 +3810,6 @@ packages: resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/fake-timers@30.4.1': resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3888,10 +3859,6 @@ packages: resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/transform@30.4.1': resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4055,7 +4022,7 @@ packages: '@json-render/react@0.19.0': resolution: {integrity: sha512-kTW6b6cSNRrlEfCUf/69SLoLn+CufC968ruge9tnQlp9pDTGG/SK8pgM541FdgwMFA4zm3s5mpM3G8rdODKc/A==} peerDependencies: - react: 19.1.0 + react: 19.2.6 '@jsonjoy.com/base64@1.1.2': resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} @@ -4317,7 +4284,7 @@ packages: resolution: {integrity: sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 '@mistralai/mistralai@2.2.6': resolution: {integrity: sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==} @@ -4335,8 +4302,8 @@ packages: engines: {node: '>=20'} peerDependencies: '@modelcontextprotocol/sdk': ^1.24.0 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 zod: 4.4.3 peerDependenciesMeta: react: @@ -4581,8 +4548,8 @@ packages: cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm-eabi@0.138.0': - resolution: {integrity: sha512-hSYAD+F9W2Qh8SETMqBsQRx6YHvB4z+i/i36shlC7tfdZQauMs4vf3G/EQwKOkNlN7rkTiKINvsNmQb9q2MWcQ==} + '@oxc-parser/binding-android-arm-eabi@0.141.0': + resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] @@ -4599,8 +4566,8 @@ packages: cpu: [arm64] os: [android] - '@oxc-parser/binding-android-arm64@0.138.0': - resolution: {integrity: sha512-Ns5LLTp8cVyP8DsYqD482h0HE84xiGYRgtm7g4LtTinq209NAiMF768e/8r2NHaa0UMirS5mrT1m1VwiVmBi4Q==} + '@oxc-parser/binding-android-arm64@0.141.0': + resolution: {integrity: sha512-a4XDQ27ZT7e7zwAlxJDTiCA7IBGWDuy2+MhFq85Of7XlBSmpkfcBFml11q0Zx6f7RMuI0B4xCtt2ytBS4yOptg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -4617,8 +4584,8 @@ packages: cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-arm64@0.138.0': - resolution: {integrity: sha512-Yka0m4YhKUHBIZufafSLAeO+DUrfHPtNXBlZSj7DxshquIl41x/a+i/MbRnbOy8heuLiYU1STa6h0FAAzT7Pbw==} + '@oxc-parser/binding-darwin-arm64@0.141.0': + resolution: {integrity: sha512-m/kVk6rzYmBeHYnz+1Y5fod00AVTTxMbC71azFfm/zjx1j9XxwKtA0+VfkKuVMC8rbghb9TtfevnuWZa9OuPEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -4635,8 +4602,8 @@ packages: cpu: [x64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.138.0': - resolution: {integrity: sha512-MWLUZZzmNRUqTWueZF27ncreaZ1wZ0gboWL2QMPxRQA2xgOmBPlGg2H9pAKJSPBlwEHcWa9TdWRiehAS+yls8w==} + '@oxc-parser/binding-darwin-x64@0.141.0': + resolution: {integrity: sha512-o0X+6KZlfucWU/v5oKRQPwdFXsXAjW8jmpo/Gpw/qyKsbKtlfkHoeH9Bjp/m13TwjewvJnCkwF0DWzgpC4HjTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -4653,8 +4620,8 @@ packages: cpu: [x64] os: [freebsd] - '@oxc-parser/binding-freebsd-x64@0.138.0': - resolution: {integrity: sha512-Vae5tzsrzZ/lCDVCZUMi/vzSiiHEgcOEfsyIfWOHmjZ2ji+gT+n96T757yX5/f7/7JIJuiannAHJKV5ARaF6ng==} + '@oxc-parser/binding-freebsd-x64@0.141.0': + resolution: {integrity: sha512-W5KbTnNkTMMMylqj6dYqnsXvkmESVPodPKYLJ5zdzIPdl9fUJtolkpUeSzYEbGGYB4a4A4avl3EePnZ/wLIdJg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -4671,8 +4638,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-gnueabihf@0.138.0': - resolution: {integrity: sha512-qkU8wv5mYexrCw0X4DHFgxGbRScwGLIIKUkHXU7xXEiLoMnQzELak2gujxfa9GFrlEgPjbyLUDFHWm67Zs38ng==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + resolution: {integrity: sha512-g3dtbJa8zeOGK36Sr9cQavsdi5H/ie2hVjrSjIxsNAR1qZA40ZYVXnfdfoMAlq8CmB9qFL1yhsSCUHeNmdmt8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -4689,8 +4656,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.138.0': - resolution: {integrity: sha512-3HgULIvoDV7h2ZfVYzxQwOSOJnAjMwYmyUBzndNuLRGgBNI549ED0P6AGmN9y2TnSvrwJ+Q8zqdxqssMnGXitA==} + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + resolution: {integrity: sha512-e6hwQqd+3lvP13G2jxvFpoA7dzHcFLN+Mq47JCVMtdNHbbyBRo756JCtbbJH6ca8inTfyqZoqBmS3vhQlzAK2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -4709,8 +4676,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-gnu@0.138.0': - resolution: {integrity: sha512-pIonbH2p0KLCwz4CNPCi0xGqci4numpMQDCLJwLfsrEky7NUuByKDFhCjzE0E7vR3aj/lBjyMoTskHBo/qSg8g==} + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + resolution: {integrity: sha512-vXz2BLAuypA+4MLyBg94pzEo6THVnzYnCtAjXoihIIQo0t2pnp/AmW+SH1EI+4VbuJnC//KplIJ5yyaCGua4jA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -4730,8 +4697,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-arm64-musl@0.138.0': - resolution: {integrity: sha512-cT5L1Xz/5m6Ga1hD3922gLc+fePOauJZJdApPTI/2Vu0EmYo62uHG9V5Dq65hhgU9TW10oDi2840y9cGdd7BIg==} + '@oxc-parser/binding-linux-arm64-musl@0.141.0': + resolution: {integrity: sha512-jMkS/EztNW34HKsXIaT/SoHcmtocq/vWhwFOVduF9kduuuRIVwfwQ6uxzIO+qPKSXdd2TXt54of0BJ2zFMXnmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -4751,8 +4718,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-ppc64-gnu@0.138.0': - resolution: {integrity: sha512-hKy/vvejKk3LNE/FsRbekWejLa046//TnLWtSo7ur29NIsNbSIvnOVYIirSVC7fsd6NO8UFzwDdcoZfCyBvSBA==} + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + resolution: {integrity: sha512-vo+MR+n3zQJ6Mq92hiP084NZcgDv5iJlVR02gMf28neMvVT1tKVm7VeiW/DxhdqOi3QLeaXIk9cUcLL1qrkngw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -4772,8 +4739,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.138.0': - resolution: {integrity: sha512-bh6tjNGq0v0b9GAMu0pTv/YpTqepCFy0TIOtQHm8+41fZwLXTaB6xiEWVUSarNCXqc5kyzYcH6EOfwW1sJxJOw==} + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + resolution: {integrity: sha512-oh80w+7RuiO5gBp9Jnoa/H8Qlt3JsHL2MkW+0dwEdlDMdslVZX/YsekSK6EeyEenY66/mhCfypsNATQ7Ph3qlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -4793,8 +4760,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-riscv64-musl@0.138.0': - resolution: {integrity: sha512-HhOkddcClSTtTxY10f/mACblKcQdxWy4lYYwX12G23j+S5eiJ5y1kpo1r7kKng+2bdnCBO+lCDWOVVc9kVl9+g==} + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + resolution: {integrity: sha512-LOyEmFA8sCnYbEXP1+iQvCC/P1YXHMA/t6x1Ksp0Y9VwhLFsiBJFzV1zIxrOIE2LKaGGhDjQ29xq9cbq6omDXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -4814,8 +4781,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-s390x-gnu@0.138.0': - resolution: {integrity: sha512-5mi+wtbeJiEa4waGG88EcEGgJBBNJdDeIcayPPcrLNMXbCrgdtbb80q0Nrat7A8NglLUVzhuTAAp7K6PjmUO8Q==} + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + resolution: {integrity: sha512-3wnwk/l1CvszVE5TJR1wSl/zSEfydRqrNhn6s7Vr9IzSJpUQIroqVsIoPARHRFA+FQwkxAFDAHDAasa7v8OobQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -4835,8 +4802,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.138.0': - resolution: {integrity: sha512-ckbq3AMI7lI8AhQtE8KdqYRmzmzwKfCU12QN/PBKXO72PfWdvvZQN0hFShDX/XRNsPqjddLmvXaQMT3zfYtNlw==} + '@oxc-parser/binding-linux-x64-gnu@0.141.0': + resolution: {integrity: sha512-qtyQVAAebFq57B2tifTlel3TgGqUtsYNI/e+p6aya9rN9lOZVTDvr215fGYSA9XWooxzMxDiVxkBLk2jQHbsOQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -4856,8 +4823,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-x64-musl@0.138.0': - resolution: {integrity: sha512-JrCOzHO9BYEs5Xz5JHYBxSc/hYKxfXUj5QQb64sERSbkQot6+KEgMTOR2C9hLrhaqOui65OYcFyTTS+YxXDtnA==} + '@oxc-parser/binding-linux-x64-musl@0.141.0': + resolution: {integrity: sha512-SkGV1nKw40roEc94pv5EaaeH2ay14G6+roe8Q0wIUC1LcEKxzKW921h7+ZuZX0D3q2Mb/7aSFmxEVqnko3lPRw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -4875,8 +4842,8 @@ packages: cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-openharmony-arm64@0.138.0': - resolution: {integrity: sha512-eASMMfOOIfLHkWJRPSu8llByvVRM+c1M/lh18KjsjELM3y10+7B5iBbbrht9LdtsJXQ+mRuP/lJ7UWe3Ok3ehw==} + '@oxc-parser/binding-openharmony-arm64@0.141.0': + resolution: {integrity: sha512-cVgDM7n8QziQqOaP5hNgUYfMG7S/ZeuPxFWXnnHRv7rh025COk0rfQ6eEdKG3j/GaUuyvNZN4ifF1J8KmuXLLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -4891,8 +4858,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-wasm32-wasi@0.138.0': - resolution: {integrity: sha512-BnTCO87Iwc57NufXS7vcrkrmpN+daeCeYr1+/xgPT6HjwNs0lBmJYeFrcOs4WkNN8yscdd6Rc4FxWh3+59hAFw==} + '@oxc-parser/binding-wasm32-wasi@0.141.0': + resolution: {integrity: sha512-HggH++Fkn3OilBn+bs3jpgIFQa34oMAyUUHy0vpGum+gt1Eb5nyLc8dNU/RAPSw6lsLrx7ncKtHSZE+3Sp0l2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] @@ -4908,8 +4875,8 @@ packages: cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-arm64-msvc@0.138.0': - resolution: {integrity: sha512-+Zi47boD2wKNL0hOA47Vkwk6njMZ8sOsr4Geu/56EUtlooDh9crNOU41U6bXGS0UjC4Y72HtRA1iuB6qx1ARUw==} + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + resolution: {integrity: sha512-KLSEH9GwgbrqbJOjtGHt9STw96s+78yDzp7IDN8Lno+7Ut9sNBfZ4jYZIz4mD50qmWUjoOI7i9I6UENbhNbMZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -4926,8 +4893,8 @@ packages: cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.138.0': - resolution: {integrity: sha512-SYcV674Wi2WuoBefUFgf0PBMNlZe5IF0YZ0TnP7DK+EusMVpEWq6iz+7r64svjAb7vjthzlas0FUCSlz8YkqYg==} + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + resolution: {integrity: sha512-9UVWUOOCI/1YkiSSNjg2zyBJYM9E/t1A/8GNobd48JDn/fQ6mzxcVO3H08jb3rAaW/B1VBf8eCORTvSsO9T08g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] @@ -4944,8 +4911,8 @@ packages: cpu: [x64] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.138.0': - resolution: {integrity: sha512-QZplnCxS4vPe4StAVBtvD2bW3pELlidf0Ek6iQ/HHiCjbEtrs5pFZZfLAoPhKLJyDzyxoGAdic9bSIYrJYTZcg==} + '@oxc-parser/binding-win32-x64-msvc@0.141.0': + resolution: {integrity: sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -4963,8 +4930,8 @@ packages: '@oxc-project/types@0.127.0': resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==} - '@oxc-project/types@0.138.0': - resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} + '@oxc-project/types@0.141.0': + resolution: {integrity: sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ==} '@oxc-resolver/binding-android-arm-eabi@11.17.0': resolution: {integrity: sha512-kVnY21v0GyZ/+LG6EIO48wK3mE79BUuakHUYLIqobO/Qqq4mJsjuYXMSn3JtLcKZpN1HDVit4UHpGJHef1lrlw==} @@ -5402,124 +5369,124 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.66.0': - resolution: {integrity: sha512-f7kq8N51T4phpzqfBpA2qaVTI/KrkCmNwaj3t/97I/WLTDI+UhlP5GL9eER+zVxBhtlx5rKXWByJU1/zDAvyaw==} + '@oxlint/binding-android-arm-eabi@1.74.0': + resolution: {integrity: sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.66.0': - resolution: {integrity: sha512-xu6QO71tdDS9mjmLZ3AqhtaVHBvdmsOKkYnReNNDgh+XiwnsipeQOIxbiYOOO0iAXycJ+GK0wdMSZP/2j/AmSg==} + '@oxlint/binding-android-arm64@1.74.0': + resolution: {integrity: sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.66.0': - resolution: {integrity: sha512-HZ24VimSOC7mxuEA99e0H2FS0C1yO3+iW13jPRAk+e2njsUs3QeAXsafCDyaIrV/MirdOVez+etQNQsJE43zNQ==} + '@oxlint/binding-darwin-arm64@1.74.0': + resolution: {integrity: sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.66.0': - resolution: {integrity: sha512-awhj8ZvJrrRSnXj7V++rpZvTmnl99L6mi0B7gg7Cp7BN6cKpzuI481bHNLvXGA9GB1/oEgA3ponuyoAc6Md12A==} + '@oxlint/binding-darwin-x64@1.74.0': + resolution: {integrity: sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.66.0': - resolution: {integrity: sha512-KQF0oVV21/FjIqkRuL8Q1vh8ECsE5+ocdH5tcqTQ4ZnYuDVoYibQUNfqBjQaUsP6UIIda5Y75Wpm5p4RgQWiWw==} + '@oxlint/binding-freebsd-x64@1.74.0': + resolution: {integrity: sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.66.0': - resolution: {integrity: sha512-9u1rgwZSEXWb30vbFZzQ78HVXBo0WCKNwJ3a2InRUTNMRng+PUDIoSFmA+m4HdUfBaIqftShq8J8qHc+eE/Vig==} + '@oxlint/binding-linux-arm-gnueabihf@1.74.0': + resolution: {integrity: sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.66.0': - resolution: {integrity: sha512-Ynot2HR1bHxUaNWoC280MVTDfZuaWuP3XfSMRDhyuZrVjhzoaBCVFlw8h8qeZjWKVUBhPWFIxB7AQTlK8Z2WWg==} + '@oxlint/binding-linux-arm-musleabihf@1.74.0': + resolution: {integrity: sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.66.0': - resolution: {integrity: sha512-xCbgzciGgo+A4aQZEknsNrNiIwY7sU5SfRuMmRjPIvZAgdF34cIHiKvwOsS5XRLjlTVSFwitmq6YclTtHTfU+g==} + '@oxlint/binding-linux-arm64-gnu@1.74.0': + resolution: {integrity: sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.66.0': - resolution: {integrity: sha512-hmo+ZB/lHkR1HdDmnziNpzSLmulnUSu10VEqX2Yex7OwvoBAbjJQLvy4gIBRV3AAwWnCvAxKp5Nv1GE6LU1QMg==} + '@oxlint/binding-linux-arm64-musl@1.74.0': + resolution: {integrity: sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.66.0': - resolution: {integrity: sha512-2Invd4Uyy81mVooQC5FBtfxSNrvcX1OxbMlVQ6M2erRrNI2awFYF26YNW2yFxdVFZ4ffNOWKghtMjhnUPsXsVA==} + '@oxlint/binding-linux-ppc64-gnu@1.74.0': + resolution: {integrity: sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.66.0': - resolution: {integrity: sha512-s0iXPDQVdgayE3RGa/N2DZF7tjgg0TwEtD1sGoDxqPDGrIXgo45H0yHknT0f9A0yteASsweYZtDyTuVlM4aSag==} + '@oxlint/binding-linux-riscv64-gnu@1.74.0': + resolution: {integrity: sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.66.0': - resolution: {integrity: sha512-OekL4XFiu7RPK0JIZi8VeHgtIXPREf42t8Cy/rKEsC+P3gcqDgNAAGiyuUOpdbG4wwbfue1q4CHcCO7spSve6w==} + '@oxlint/binding-linux-riscv64-musl@1.74.0': + resolution: {integrity: sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.66.0': - resolution: {integrity: sha512-Ga1D0kj1SFslm34ThA/BdkUlyAYEnTsXyRC4pF0C5agZSwtGdHYWMTQWemUfBGp4RCG4QWXgdO+HmmmKqOtlBg==} + '@oxlint/binding-linux-s390x-gnu@1.74.0': + resolution: {integrity: sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.66.0': - resolution: {integrity: sha512-p5jfP1wUZe/IC3qpQO84n9DRnf9g3lKRtLBlQq23ykyrDglHcVx7sWmVTlPuU6SBw8mNnPzyOn022G3XZHnlww==} + '@oxlint/binding-linux-x64-gnu@1.74.0': + resolution: {integrity: sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.66.0': - resolution: {integrity: sha512-vUB/sYlYZorDL1ZD+o9mRv7zbsykrrFRtmgS6R8musZqLtrPRQn1gc1eGpuX+sfdccz42STl/AqldY6XRb2upQ==} + '@oxlint/binding-linux-x64-musl@1.74.0': + resolution: {integrity: sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.66.0': - resolution: {integrity: sha512-yde+6p/F59xRkGR9H1HfngWRif1QRJjynZK349l+UI0H6w9hL3G8/AVaTHFyTtLVQ56qtNbX2/5Dc77n1ovnOg==} + '@oxlint/binding-openharmony-arm64@1.74.0': + resolution: {integrity: sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.66.0': - resolution: {integrity: sha512-O9GLucgoTdmOrbBX+EjzNe7o/Ze5TFOvXcib6bzUOtBOmj6cV+zw18NgB+cGKAkDw1Pdqs8vGkfHbbsLuDtXWg==} + '@oxlint/binding-win32-arm64-msvc@1.74.0': + resolution: {integrity: sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.66.0': - resolution: {integrity: sha512-m3Pjwc2MfTcom4E4gOv7DyuGyt7OfGNCbmqDHd+N7EzXmP+ppHuudm2NjcA3AjV5TSeGxaguVF4SbTKHe1USYA==} + '@oxlint/binding-win32-ia32-msvc@1.74.0': + resolution: {integrity: sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.66.0': - resolution: {integrity: sha512-/DbBvw8UFBhja6PqudUjV4UtfsJr0Oa7jUjWVKB0g86lj/VwnPrkngn0sFql3c9RDA0O16dh7ozsXb6GjNAzBQ==} + '@oxlint/binding-win32-x64-msvc@1.74.0': + resolution: {integrity: sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -5630,14 +5597,14 @@ packages: resolution: {integrity: sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==} engines: {node: '>=10'} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@pierre/diffs@1.2.10': resolution: {integrity: sha512-rPeAmDWarxFVTQpaf4y6wTxjZxU44xKJKoJti2zU21P06DVd9nRHZX+xSIObLB307Qjpaesyb1x/j0z94t7vLw==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@pierre/theme@1.0.3': resolution: {integrity: sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==} @@ -5648,8 +5615,8 @@ packages: peerDependencies: '@pierre/theme': ^1.0.0 '@shikijs/themes': ^3.0.0 || ^4.0.0 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 shiki: ^3.0.0 || ^4.0.0 peerDependenciesMeta: '@pierre/theme': @@ -5708,8 +5675,8 @@ packages: resolution: {integrity: sha512-Qyd9DckDg1Z/vT3mpKyuMemJHWpYD0k0Gob7hWCNMCDSYm/NcpDS1uX8PRoh3Z7HF2kBkZ7j6HCSUIG7Ha/j4Q==} engines: {node: '>=18'} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@posthog/plugin-utils@1.1.1': resolution: {integrity: sha512-vCbaFeuwf9Pc0gI5bkCGvkOn2Bxru2KbZJtOa6loTJjanCNoMsjECEPijr7X5oln1IIg+VKnGiwV4tKY2b7NuQ==} @@ -5717,16 +5684,16 @@ packages: '@posthog/quill-charts@0.3.0-beta.19': resolution: {integrity: sha512-SqZQr+zclHTjdCeZQh+mrH9nzZk1dFDPC9++1QYh0IdMa/dEFzxCkQgIuY7BpRsPv4YIB5WlTIB4XT/BujR2xA==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@posthog/quill@0.3.0-beta.24': resolution: {integrity: sha512-lBnnFqX3aVNXPPc5j8pO2cGr99IeClIr2ByVTdote477Bnqwt8HDX7jbFxCwiUr8ARnuSTvhDrqeagZzplwE9Q==} engines: {node: '>=20'} peerDependencies: '@base-ui/react': ^1.3.0 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 tailwindcss: ^4.0.0 '@posthog/rollup-plugin@1.4.5': @@ -5795,8 +5762,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5808,8 +5775,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5821,8 +5788,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5834,8 +5801,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5847,8 +5814,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5860,8 +5827,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5873,8 +5840,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5886,8 +5853,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5899,8 +5866,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5911,7 +5878,7 @@ packages: resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5921,8 +5888,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5933,7 +5900,7 @@ packages: resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5943,8 +5910,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5955,7 +5922,7 @@ packages: resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5965,8 +5932,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5978,8 +5945,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -5990,7 +5957,7 @@ packages: resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6000,8 +5967,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6013,8 +5980,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6026,8 +5993,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6037,13 +6004,13 @@ packages: '@radix-ui/react-icons@1.3.2': resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} peerDependencies: - react: 19.1.0 + react: 19.2.6 '@radix-ui/react-id@1.1.1': resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6053,8 +6020,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6066,8 +6033,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6079,8 +6046,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6092,8 +6059,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6105,8 +6072,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6118,8 +6085,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6131,8 +6098,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6144,8 +6111,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6157,8 +6124,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6170,8 +6137,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6183,8 +6150,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6196,8 +6163,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6209,8 +6176,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6222,8 +6189,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6235,8 +6202,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6248,8 +6215,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6261,8 +6228,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6274,8 +6241,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6287,28 +6254,19 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.0': - resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} - peerDependencies: - '@types/react': ^19.2.15 - react: 19.1.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6317,7 +6275,7 @@ packages: resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6327,8 +6285,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6340,8 +6298,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6353,8 +6311,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6366,8 +6324,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6379,8 +6337,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6392,8 +6350,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6405,8 +6363,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6417,7 +6375,7 @@ packages: resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6426,7 +6384,7 @@ packages: resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6435,7 +6393,7 @@ packages: resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6444,7 +6402,7 @@ packages: resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6453,7 +6411,7 @@ packages: resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6462,7 +6420,7 @@ packages: resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6471,7 +6429,7 @@ packages: resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6480,7 +6438,7 @@ packages: resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6489,7 +6447,7 @@ packages: resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6499,8 +6457,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -6515,16 +6473,16 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - '@react-grab/cli@0.1.48': - resolution: {integrity: sha512-KXRZFN0b78BeVa4Tq1FC9kiXPpC5lS4pQp/mvQ1azy9dZUJ3zfc7Ei84+yvGh+WoYdceMCFxXfBp6qhU/G056g==} + '@react-grab/cli@0.1.50': + resolution: {integrity: sha512-Px/Hwhhyk2PubCA4ZaRFsfvwxhbxXsetJyvqC6aFFi8WhJhA+oVC33aTzuAeWmM3fhb4/8ce8YsHXI1d6ChcKg==} hasBin: true '@react-native-async-storage/async-storage@2.2.0': @@ -6535,113 +6493,103 @@ packages: '@react-native-community/netinfo@12.0.1': resolution: {integrity: sha512-P/3caXIvfYSJG8AWJVefukg+ZGRPs+M4Lp3pNJtgcTYoJxCjWrKQGNnCkj/Cz//zWa/avGed0i/wzm0T8vV2IQ==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '>=0.59' - '@react-native/assets-registry@0.81.5': - resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==} - engines: {node: '>= 20.19.4'} + '@react-native-masked-view/masked-view@0.3.2': + resolution: {integrity: sha512-XwuQoW7/GEgWRMovOQtX3A4PrXhyaZm0lVUiY8qJDvdngjLms9Cpdck6SmGAUNqQwcj2EadHC1HwL0bEyoa/SQ==} + peerDependencies: + react: 19.2.6 + react-native: '>=0.57' - '@react-native/babel-plugin-codegen@0.81.5': - resolution: {integrity: sha512-oF71cIH6je3fSLi6VPjjC3Sgyyn57JLHXs+mHWc9MoCiJJcM4nqsS5J38zv1XQ8d3zOW2JtHro+LF0tagj2bfQ==} - engines: {node: '>= 20.19.4'} + '@react-native/assets-registry@0.86.0': + resolution: {integrity: sha512-nIaXbm2jX1OTYp0qbviJ3O6KZivoE8z3BnhUQ2LsqfZSWRoOK/n1qsiAr6oALiNKWnXY3j2KPwtYORnZzp8xew==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/babel-preset@0.81.5': - resolution: {integrity: sha512-UoI/x/5tCmi+pZ3c1+Ypr1DaRMDLI3y+Q70pVLLVgrnC3DHsHRIbHcCHIeG/IJvoeFqFM2sTdhSOLJrf8lOPrA==} - engines: {node: '>= 20.19.4'} + '@react-native/babel-plugin-codegen@0.86.0': + resolution: {integrity: sha512-qdsABWNW7uTll90l4Vh03gjeyu3WVDi2CyiiyvYGMRDcoYbjbQi6df3BMAm9lQI2yslZ1T14LlDDAsgTwNxplA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/babel-preset@0.86.0': + resolution: {integrity: sha512-bYQcWiPySNvF4dns9Ls9gMmwgq66ohvM9Fwc/Kn8r85t66UNHxch3p1QwPiSorDelFauZwJbgo9+ReibTgvpbA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/codegen@0.81.5': - resolution: {integrity: sha512-a2TDA03Up8lpSa9sh5VRGCQDXgCTOyDOFH+aqyinxp1HChG8uk89/G+nkJ9FPd0rqgi25eCTR16TWdS3b+fA6g==} - engines: {node: '>= 20.19.4'} + '@react-native/codegen@0.86.0': + resolution: {integrity: sha512-uTs9DBo3+/lUqinsGZK0FKJRBVClrwMXoZToaDxE1Q2SL2e55vs2GwyZfIKzPl5uJnbu4PfFMIp0/mLXLWUMuA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.81.5': - resolution: {integrity: sha512-yWRlmEOtcyvSZ4+OvqPabt+NS36vg0K/WADTQLhrYrm9qdZSuXmq8PmdJWz/68wAqKQ+4KTILiq2kjRQwnyhQw==} - engines: {node: '>= 20.19.4'} + '@react-native/community-cli-plugin@0.86.0': + resolution: {integrity: sha512-Jv8p1ebEPfTzs8gmrjsdT2XMXFfeAg45Pman+XPLFGaSeGAZkutRFRyX9Cs9aGTSOyIA9YPJ6vDNb1ayTf1FKQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@react-native-community/cli': '*' - '@react-native/metro-config': '*' + '@react-native/metro-config': 0.86.0 peerDependenciesMeta: '@react-native-community/cli': optional: true '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.81.5': - resolution: {integrity: sha512-bnd9FSdWKx2ncklOetCgrlwqSGhMHP2zOxObJbOWXoj7GHEmih4MKarBo5/a8gX8EfA1EwRATdfNBQ81DY+h+w==} - engines: {node: '>= 20.19.4'} + '@react-native/debugger-frontend@0.86.0': + resolution: {integrity: sha512-7Mb3nDfyJeys+ELF75Ageu7VKERlnIMoO+aNPoXqTXvz+b41L6l2CqMyLpDHxkBSlenij6gEepPNgaIyWHbJZw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/dev-middleware@0.81.5': - resolution: {integrity: sha512-WfPfZzboYgo/TUtysuD5xyANzzfka8Ebni6RIb2wDxhb56ERi7qDrE4xGhtPsjCL4pQBXSVxyIlCy0d8I6EgGA==} - engines: {node: '>= 20.19.4'} + '@react-native/debugger-shell@0.86.0': + resolution: {integrity: sha512-Y0zEkZzLz8ou6o/VLml1A31X/rMgc6DRjwxwzPMa94qRTMY070WeBCNTITQo4kKTBAUgbxh07oXPQqp0Tpja8w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - '@react-native/gradle-plugin@0.81.5': - resolution: {integrity: sha512-hORRlNBj+ReNMLo9jme3yQ6JQf4GZpVEBLxmTXGGlIL78MAezDZr5/uq9dwElSbcGmLEgeiax6e174Fie6qPLg==} - engines: {node: '>= 20.19.4'} + '@react-native/dev-middleware@0.86.0': + resolution: {integrity: sha512-20pTO6yTybmvXvro520H6C7jydIQnLKOl5qFtVEcHSdFrY63r3OGei+Rx9bILgSRmH6jgnfEcijcMx7pwWuQtw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/gradle-plugin@0.86.0': + resolution: {integrity: sha512-a1RcfaEDqWExCGfCwadIxt4l8FvKYgFqeMf2uzeKyAOnb+vTGNIeCvifFL2MqvgaeYxlER437HbMIajGcuJ1pQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/js-polyfills@0.86.0': + resolution: {integrity: sha512-zYy/Cjd1VTnZ2iCNaG9bDF9C3l2ntESiPRscjIlI5FKugu6aeTwsDSv1aI8Bc4Kp3vEdoVg+UQhLAhE4svREaQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + + '@react-native/metro-babel-transformer@0.86.0': + resolution: {integrity: sha512-SjKej3E5qIahqo/G+rSOrmJUQM44RyKtWtO+VfmKAAMoJWkBFomM22hTLKCIS5cdbIAJ9COAmU+KAi2wVSO0wQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} + peerDependencies: + '@babel/core': '*' - '@react-native/js-polyfills@0.81.5': - resolution: {integrity: sha512-fB7M1CMOCIUudTRuj7kzxIBTVw2KXnsgbQ6+4cbqSxo8NmRRhA0Ul4ZUzZj3rFd3VznTL4Brmocv1oiN0bWZ8w==} - engines: {node: '>= 20.19.4'} + '@react-native/metro-config@0.86.0': + resolution: {integrity: sha512-7v+xbTeEci9ZcQ/Z1OqI4RXcqN69wSMDYL5BAMvOReZ7U04+aDQ0/SQhClYPn6x2/RxM4WzMKSAuNyLKqvYVtw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} '@react-native/normalize-colors@0.74.89': resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} - '@react-native/normalize-colors@0.81.5': - resolution: {integrity: sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==} + '@react-native/normalize-colors@0.86.0': + resolution: {integrity: sha512-kG0wfCGghUKlfxkJyyHCDVutWVYWK7/DG58ojA/4v9EfulgF+osuSQmlbNb3rcKX58qutm7JcldSeVLgGFha9g==} - '@react-native/virtualized-lists@0.81.5': - resolution: {integrity: sha512-UVXgV/db25OPIvwZySeToXD/9sKKhOdkcWmmf4Jh8iBZuyfML+/5CasaZ1E7Lqg6g3uqVQq75NqIwkYmORJMPw==} - engines: {node: '>= 20.19.4'} + '@react-native/virtualized-lists@0.86.0': + resolution: {integrity: sha512-4/ZLXdf/OSpPDVO0AsQ1SJdRIzt5t9BNQ46QwGgxvX7/cirYR5k8KXctNGGgW8lQo2gZChEfY2zFCZg9nM/jiw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 - react-native: '*' + react: 19.2.6 + react-native: 0.86.0 peerDependenciesMeta: '@types/react': optional: true - '@react-navigation/bottom-tabs@7.12.0': - resolution: {integrity: sha512-/GtOfVWRligHG0mvX39I1FGdUWeWl0GVF2okEziQSQj0bOTrLIt7y44C3r/aCLkEpTVltCPGM3swqGTH3UfRCw==} - peerDependencies: - '@react-navigation/native': ^7.1.28 - react: 19.1.0 - react-native: '*' - react-native-safe-area-context: '>= 4.0.0' - react-native-screens: '>= 4.0.0' - '@react-navigation/core@7.14.0': resolution: {integrity: sha512-tMpzskBzVp0E7CRNdNtJIdXjk54Kwe/TF9ViXAef+YFM1kSfGv4e/B2ozfXE+YyYgmh4WavTv8fkdJz1CNyu+g==} peerDependencies: - react: 19.1.0 - - '@react-navigation/elements@2.9.5': - resolution: {integrity: sha512-iHZU8rRN1014Upz73AqNVXDvSMZDh5/ktQ1CMe21rdgnOY79RWtHHBp9qOS3VtqlUVYGkuX5GEw5mDt4tKdl0g==} - peerDependencies: - '@react-native-masked-view/masked-view': '>= 0.2.0' - '@react-navigation/native': ^7.1.28 - react: 19.1.0 - react-native: '*' - react-native-safe-area-context: '>= 4.0.0' - peerDependenciesMeta: - '@react-native-masked-view/masked-view': - optional: true - - '@react-navigation/native-stack@7.12.0': - resolution: {integrity: sha512-XmNJsPshjkNsahgbxNgGWQUq4s1l6HqH/Fei4QsjBNn/0mTvVrRVZwJ1XrY9YhWYvyiYkAN6/OmarWQaQJ0otQ==} - peerDependencies: - '@react-navigation/native': ^7.1.28 - react: 19.1.0 - react-native: '*' - react-native-safe-area-context: '>= 4.0.0' - react-native-screens: '>= 4.0.0' + react: 19.2.6 '@react-navigation/native@7.1.28': resolution: {integrity: sha512-d1QDn+KNHfHGt3UIwOZvupvdsDdiHYZBEj7+wL2yDVo3tMezamYy60H9s3EnNVE1Ae1ty0trc7F2OKqo/RmsdQ==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' '@react-navigation/routers@7.5.3': @@ -6935,6 +6883,10 @@ packages: resolution: {integrity: sha512-IrOyMzlOyBkWtnVYb54sALf2f8WVqyyp7woRfHw8c3IMwUr5AskGOi7k2rjmUXO3Q0UkfHNVarexiEfo8ZqTsg==} engines: {node: '>=18'} + '@shaderfrog/glsl-parser@7.0.1': + resolution: {integrity: sha512-8mpfsoPeRhesY3pOrzNZBL8uG6N5GVX1EHLBYbd4gzKs+c7vaEIqpTNK5VrffU33qQN4cwpP2v3u4aPPBU32sw==} + engines: {node: '>=16'} + '@shikijs/core@3.23.0': resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} @@ -6994,9 +6946,6 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@sinonjs/fake-timers@15.4.0': resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} @@ -7090,16 +7039,16 @@ packages: '@storybook/icons@2.0.2': resolution: {integrity: sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@storybook/react-dom-shim@10.4.1': resolution: {integrity: sha512-6QFqfDNH4DMrt7yHKRfpqRopsVUc/Az+sXIdJ39IetYnHUxL3nW4NVaPc6uy/8Qi8urzUyEXL/nn7cpSIP2aPQ==} peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 storybook: ^10.4.1 peerDependenciesMeta: '@types/react': @@ -7110,8 +7059,8 @@ packages: '@storybook/react-vite@10.4.1': resolution: {integrity: sha512-zY6OzaXvXqBIUyc5ySE55/LAPQiF+o9ZyhQI978WMu4mY/fL7FpQ+ZVHRUCCgz/wTXtqE9jJwd/N10HI1kD0/Q==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 storybook: ^10.4.1 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -7120,8 +7069,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 storybook: ^10.4.1 typescript: '>= 4.9.x' peerDependenciesMeta: @@ -7537,12 +7486,12 @@ packages: '@tanstack/react-query@5.101.0': resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 '@tanstack/react-query@5.90.20': resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==} peerDependencies: - react: 19.1.0 + react: 19.2.6 '@tanstack/react-router-devtools@1.167.0': resolution: {integrity: sha512-nGw095EG7IHx0h5NtlEmzf6vcCTaFNPWdTSuDKazajhN0ct/v/TkekJ9J6KYUCeV1a8/2ZmToc58M+0rrOyn7w==} @@ -7550,8 +7499,8 @@ packages: peerDependencies: '@tanstack/react-router': ^1.170.0 '@tanstack/router-core': ^1.170.0 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@tanstack/router-core': optional: true @@ -7560,20 +7509,20 @@ packages: resolution: {integrity: sha512-GawYz7HEjj8rTUUDoT/SemDEVm63pZUO+2mOcXHY9Jl3EwMS5gFBnPu/2UvcrwRm1jN1k79fokc0d4aFmrLatg==} engines: {node: '>=20.19'} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@tanstack/react-store@0.9.3': resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@tanstack/react-virtual@3.14.2': resolution: {integrity: sha512-IpWnmCLvuymRfeeLNVXIzNEYBFLpd3drVIS91sqV78VTZFyldlChkOocZRCPp1B+Wnk09bcLNme8WaMU/9/9bQ==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@tanstack/router-core@1.171.13': resolution: {integrity: sha512-+NOwEj1kO/6IGmpHRIZHasYxYWpyBQGNIZAST9aNrk9Q3YlU9SgqVnl1pbLa9qAKfeNdXQIRve0RQb/0kyDeDA==} @@ -7641,9 +7590,9 @@ packages: engines: {node: '>=18'} peerDependencies: jest: '>=29.0.0' - react: 19.1.0 + react: 19.2.6 react-native: '>=0.71' - react-test-renderer: 19.1.0 + react-test-renderer: 19.2.6 peerDependenciesMeta: jest: optional: true @@ -7655,8 +7604,8 @@ packages: '@testing-library/dom': ^10.0.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -7824,8 +7773,8 @@ packages: '@tiptap/pm': ^3.19.0 '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 '@tiptap/starter-kit@3.19.0': resolution: {integrity: sha512-dTCkHEz+Y8ADxX7h+xvl6caAj+3nII/wMB1rTQchSuNKqJTOrzyUsCWm094+IoZmLT738wANE0fRIgziNHs/ug==} @@ -7874,7 +7823,7 @@ packages: '@tanstack/react-query': ^5.80.3 '@trpc/client': 11.17.0 '@trpc/server': 11.17.0 - react: 19.1.0 + react: 19.2.6 typescript: '>=5.7.2' '@ts-morph/common@0.27.0': @@ -7982,9 +7931,6 @@ packages: '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -8245,14 +8191,6 @@ packages: cpu: [x64] os: [win32] - '@urql/core@5.2.0': - resolution: {integrity: sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==} - - '@urql/exchange-retry@1.3.2': - resolution: {integrity: sha512-TQMCz2pFJMfpNxmSfX1VSfTjwUIFx/mL+p1bnfM1xjjdla7Z+KnGMW/EhFbpckp3LyWAH4PgOsMwOMnIN+MBFg==} - peerDependencies: - '@urql/core': ^5.0.0 - '@vitejs/plugin-react@4.7.0': resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -8490,6 +8428,11 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-cli-detector@0.1.4: + resolution: {integrity: sha512-qPgevFvpaQoBaRJVKzr8R7h1WPvV3DtbgRIQlne4le66KBzXx5hNBwo/+NTw67LgkKBlhCzksrdautpUdlls0Q==} + engines: {node: '>=18.18'} + hasBin: true + agent-install@0.0.5: resolution: {integrity: sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ==} hasBin: true @@ -8548,6 +8491,10 @@ packages: resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-regex@4.1.1: resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} engines: {node: '>=6'} @@ -8640,9 +8587,6 @@ packages: resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} engines: {node: '>=12.0.0'} - assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -8662,9 +8606,6 @@ packages: resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} engines: {node: '>=0.12.0'} - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -8678,9 +8619,9 @@ packages: atomically@2.1.0: resolution: {integrity: sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + auto-bind@5.0.1: + resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} await-to-js@3.0.0: resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} @@ -8702,30 +8643,16 @@ packages: babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - babel-jest@30.4.1: resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - babel-plugin-istanbul@7.0.1: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-plugin-jest-hoist@30.4.0: resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -8751,8 +8678,11 @@ packages: babel-plugin-react-native-web@0.21.2: resolution: {integrity: sha512-SPD0J6qjJn8231i0HZhlAGH6NORe+QvRSQM2mwQEzJ2Fb3E4ruWTiiicPlHjmeWShDXLcvoorOCXjeR7k/lyWA==} - babel-plugin-syntax-hermes-parser@0.29.1: - resolution: {integrity: sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==} + babel-plugin-syntax-hermes-parser@0.36.0: + resolution: {integrity: sha512-LhD0xdoedDw7ansQgXbB2DADLZIK/LRXuWNBPuVzMc5S2WK5GyT89tCM+cQzxFGO0mGyLK6D5TrVOJJzAoDy8Q==} + + babel-plugin-syntax-hermes-parser@0.36.1: + resolution: {integrity: sha512-ycduwJbvdvIMmVvlAZqGggS+pm5Eu4Bk9pcV9Sm2Z4PJNRVsKkv0g7vHj+LeuC1gHTeF67sJXFOq61IlqCa2hA==} babel-plugin-transform-flow-enums@0.0.2: resolution: {integrity: sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==} @@ -8762,23 +8692,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-expo@54.0.10: - resolution: {integrity: sha512-wTt7POavLFypLcPW/uC5v8y+mtQKDJiyGLzYCjqr9tx0Qc3vCXcDKk1iCFIj/++Iy5CWhhTflEa7VvVPNWeCfw==} + babel-preset-expo@57.0.4: + resolution: {integrity: sha512-EkFcNoE23HVzQT6ZNXs/adN8+G7rqEAF6tQn6LpRPYa1YY7wmX5GxhJF0kaYMNtBndNrMTN4+0rYE17VG13KFg==} peerDependencies: '@babel/runtime': ^7.20.0 expo: '*' + expo-widgets: ^57.0.6 react-refresh: '>=0.14.0 <1.0.0' peerDependenciesMeta: '@babel/runtime': optional: true expo: optional: true - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 + expo-widgets: + optional: true babel-preset-jest@30.4.0: resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} @@ -8813,10 +8740,6 @@ packages: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} - better-opn@3.0.2: - resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} - engines: {node: '>=12.0.0'} - better-sqlite3@12.10.1: resolution: {integrity: sha512-HfFtzCqnSfwB3+HroF6PSKzyh+7RfNMGPCzHFUZXRlvrPCb4P3cvxKZNN43Sr7IrkofqQZM+gIvffGpA8VvqgA==} engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} @@ -8842,12 +8765,12 @@ packages: bippy@0.5.42: resolution: {integrity: sha512-K3tpfO9uGQB2k/Vi5P6jgfrnXvO/FAQNUE2tqKjQmT0a93fJCysMGLgJmRKzYYfybAoOtwWwmKm0vw/uXE0hMw==} peerDependencies: - react: 19.1.0 + react: 19.2.6 - bippy@0.5.43: - resolution: {integrity: sha512-Tvu7b1M7+d8b9/YHaCeODEsi2CgbuoBql+dWSBrNnCuqJ1gMUeY3i0r+319hvjjl5GVBP6FFWxrKnq3fhZER0w==} + bippy@0.6.1: + resolution: {integrity: sha512-ky4m94Y/KfsddjGkKTsV4uFjZqkJjpOjQ2t5gKPdX6XH1MNxMNX5FrVefsxV4lpjemEmEdwe0e0YbzAMNs3oUQ==} peerDependencies: - react: 19.1.0 + react: 19.2.6 bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -8967,10 +8890,6 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.9: - resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} - engines: {node: '>= 0.4'} - call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -9071,8 +8990,8 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - chromium-edge-launcher@0.2.0: - resolution: {integrity: sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==} + chromium-edge-launcher@0.3.0: + resolution: {integrity: sha512-p03azHlGjtyRvFEee3cyvtsRYdniSkwjkzmM/KmVnqT5d7QkkwpJBhis/zCLMYdQMVJ5tt140TBNqqrZPaWeFA==} chromium-pickle-js@0.2.0: resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} @@ -9105,10 +9024,18 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} + cli-boxes@4.0.1: + resolution: {integrity: sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw==} + engines: {node: '>=18.20 <19 || >=20.10'} + cli-cursor@2.1.0: resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} engines: {node: '>=4'} + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -9125,6 +9052,10 @@ packages: resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} engines: {node: '>=18'} + cli-truncate@6.1.1: + resolution: {integrity: sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw==} + engines: {node: '>=22'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -9153,8 +9084,8 @@ packages: cmdk@1.1.1: resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} @@ -9163,6 +9094,10 @@ packages: code-block-writer@13.0.3: resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} + code-excerpt@4.0.0: + resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + collect-v8-coverage@1.0.3: resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} @@ -9292,6 +9227,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convert-to-spaces@2.0.1: + resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} @@ -9359,10 +9298,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crypto-random-string@2.0.0: - resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} - engines: {node: '>=8'} - css-in-js-utils@3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} @@ -9551,10 +9486,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -9575,8 +9506,8 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - deslop-js@0.8.3: - resolution: {integrity: sha512-axNV/iX3Zq9xt0MYesmbBGxneeeY/HrYgXTsaM4+GOrdxXP9JyCfTdC4j8zx09bBML94oSIpFNyn9U1f0oEPqQ==} + deslop-js@0.9.2: + resolution: {integrity: sha512-rGhQ17gHnmsjG5KFJM4+oN4bOxktHiPGqzJxKqWt0Qw/NLZIsEgLH1wIo1tTXRyN/MNwhchKbfUieFiWyAh0pQ==} destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} @@ -9627,6 +9558,9 @@ packages: dmg-builder@26.15.3: resolution: {integrity: sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==} + dnssd-advertise@1.1.6: + resolution: {integrity: sha512-Ndrrf6BMPalkQPd/zubL+4YghH2J9NspapQ09uDXwYbvOPkP0oaqf5CkcwJ0b50kS2O3ul6yVu+jz+RY62Cejg==} + doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -10001,10 +9935,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - env-editor@0.4.2: - resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==} - engines: {node: '>=8'} - env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -10048,6 +9978,9 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -10195,9 +10128,6 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - exec-async@2.2.0: - resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -10241,200 +10171,206 @@ packages: resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - expo-application@7.0.8: - resolution: {integrity: sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==} + expo-application@57.0.2: + resolution: {integrity: sha512-q31YwcXyymviAmdrtDfAg3Dld4VMxLCNAfgMHip7vZpPX4lzF/AfwsywqBJUAjQnJknzaNAkzqvMVjO5XmKYDA==} peerDependencies: expo: '*' - expo-asset@12.0.12: - resolution: {integrity: sha512-CsXFCQbx2fElSMn0lyTdRIyKlSXOal6ilLJd+yeZ6xaC7I9AICQgscY5nj0QcwgA+KYYCCEQEBndMsmj7drOWQ==} + expo-asset@57.0.7: + resolution: {integrity: sha512-TA6MRQq1HL0N6KGvNMzL6djdH5tGJ/eHPIhML+6bVu52mnXldmup2swdvP37zDnvoMUUXRVGsvwVHFmvhCbW6Q==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-auth-session@7.0.10: - resolution: {integrity: sha512-XDnKkudvhHSKkZfJ+KkodM+anQcrxB71i+h0kKabdLa5YDXTQ81aC38KRc3TMqmnBDHAu0NpfbzEVd9WDFY3Qg==} + expo-audio@57.0.3: + resolution: {integrity: sha512-FzO0gnVmlrKmNoox7xc/795uNiuuqnYBovo2kgnNICDKJ0kDi1Y5UJjqX+NATxCelZcNv5BtWs3POkKJADhNCA==} peerDependencies: - react: 19.1.0 + expo: '*' + expo-asset: '*' + react: 19.2.6 react-native: '*' - expo-av@16.0.8: - resolution: {integrity: sha512-cmVPftGR/ca7XBgs7R6ky36lF3OC0/MM/lpgX/yXqfv0jASTsh7AYX9JxHCwFmF+Z6JEB1vne9FDx4GiLcGreQ==} + expo-auth-session@57.0.5: + resolution: {integrity: sha512-4u/CIMCrQ88QO9AKSaw61Vva+Gmtil254xBjDB2SuAeyN/I9mNxZKkG0xtcm3iDSmJFMuHkYDKNo3RvTn3sz+Q==} peerDependencies: - expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - react-native-web: '*' - peerDependenciesMeta: - react-native-web: - optional: true - expo-camera@55.0.15: - resolution: {integrity: sha512-WRVsZf+2p7EsxudwyiUMYijJS8M98t/BVP6yG7N+08JSUotkGjmZcemom1gM36uy27P8QsSVP0hD+FravmQiBA==} + expo-camera@57.0.3: + resolution: {integrity: sha512-Q+3aZ63eQCkdB6/FZrO/lfacNAg/j8JCeKQL2nBdf6vBeOo1Y2PKYx1/vK+U5LaRnIo/0tMGmCOzZ1JGhTeMIw==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' react-native-web: '*' peerDependenciesMeta: react-native-web: optional: true - expo-clipboard@55.0.13: - resolution: {integrity: sha512-PrOmmuVsGW4bAkNQmGKtxMXj3invsfN+jfIKmQxHwE/dn7ODqwFWviUTa+PMUjP3XZmYCDLyu/i0GLeu7HF9Ew==} + expo-clipboard@57.0.1: + resolution: {integrity: sha512-HWICri4+1ao7S6QEfcorxVumXDiDnx1guGGewjZgGJWLGxFYs0RgH8ujBs+lkTzBkMmlwADaWSlaesR+nDJt5Q==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-constants@18.0.13: - resolution: {integrity: sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==} + expo-constants@57.0.7: + resolution: {integrity: sha512-ShDwaKnh3UieCQ/dG0kO8PuicTTatn3WDGmXbq/fukyzPXWhdUxf37DINVDQS6D3DDG7nfqItij+QvnDHSQhTg==} peerDependencies: expo: '*' react-native: '*' - expo-crypto@15.0.8: - resolution: {integrity: sha512-aF7A914TB66WIlTJvl5J6/itejfY78O7dq3ibvFltL9vnTALJ/7LYHvLT4fwmx9yUNS6ekLBtDGWivFWnj2Fcw==} + expo-crypto@57.0.1: + resolution: {integrity: sha512-xwegXQw3ATgeL1ZuqbSNrGzOeG+zNeh6Z6DSJk825Qpa3TEQQ1kG3ioE1p3g/SNF373BAVz2iBKUTSytlIbBRA==} peerDependencies: expo: '*' - expo-dev-client@6.0.20: - resolution: {integrity: sha512-5XjoVlj1OxakNxy55j/AUaGPrDOlQlB6XdHLLWAw61w5ffSpUDHDnuZzKzs9xY1eIaogOqTOQaAzZ2ddBkdXLA==} + expo-dev-client@57.0.9: + resolution: {integrity: sha512-DDqinc0EHMsqDUqkfad6c3pc5vr2oCxwAojSGVSBZe5paRIPgQTEWA+x76Qel13F1LO2LSG+DLs5W1vk3JwUow==} peerDependencies: expo: '*' - expo-dev-launcher@6.0.20: - resolution: {integrity: sha512-a04zHEeT9sB0L5EB38fz7sNnUKJ2Ar1pXpcyl60Ki8bXPNCs9rjY7NuYrDkP/irM8+1DklMBqHpyHiLyJ/R+EA==} + expo-dev-launcher@57.0.9: + resolution: {integrity: sha512-f2LhS6FaQBKACSNQ1opPR7bg6ehAJnrDuEkewqINJkTDryhGY7Y6P21uoXWJzEc9IFJAjUKan8A8AOztibGtMQ==} peerDependencies: expo: '*' + react-native: '*' - expo-dev-menu-interface@2.0.0: - resolution: {integrity: sha512-BvAMPt6x+vyXpThsyjjOYyjwfjREV4OOpQkZ0tNl+nGpsPfcY9mc6DRACoWnH9KpLzyIt3BOgh3cuy/h/OxQjw==} + expo-dev-menu-interface@57.0.0: + resolution: {integrity: sha512-F47VdzOHYc19FhI/jBgctpO8a5UskTIxG6a1E5t3W5gF8VImuvBQffdXXfLHhsuCl7dS3v3U0R45cleeVXO1Zg==} peerDependencies: expo: '*' - expo-dev-menu@7.0.18: - resolution: {integrity: sha512-4kTdlHrnZCAWCT6tZRQHSSjZ7vECFisL4T+nsG/GJDo/jcHNaOVGV5qPV9wzlTxyMk3YOPggRw4+g7Ownrg5eA==} + expo-dev-menu@57.0.9: + resolution: {integrity: sha512-OGU3Apwx1fr8HDUD9F66tk/CuxiIhYkHci4837vX7ZckS6yI/yL6gvcmgqvNCoE4pDV9demjaGEZmSJ2xTrUhg==} peerDependencies: expo: '*' + react-native: '*' - expo-device@8.0.10: - resolution: {integrity: sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA==} + expo-device@57.0.1: + resolution: {integrity: sha512-jyEMDUticH+dhcL3GHa2aiifOvGXJsmb3oVT2R2q4i8bN7Bddy61+NkpMmuS2VAZrvoLQwf0TJJ/1vi1ukvutA==} peerDependencies: expo: '*' - expo-document-picker@14.0.8: - resolution: {integrity: sha512-3tyQKpPqWWFlI8p9RiMX1+T1Zge5mEKeBuXWp1h8PEItFMUDSiOJbQ112sfdC6Hxt8wSxreV9bCRl/NgBdt+fA==} + expo-document-picker@57.0.1: + resolution: {integrity: sha512-qBwM5oxDZ3I9kwFD3pUE1oK/WNv9artoEKO6UpqhQgNRr0XA1ALRVWYjkF4+ge9lUNDRehjTm/jenINkzqg84g==} peerDependencies: expo: '*' - expo-file-system@19.0.21: - resolution: {integrity: sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==} + expo-file-system@57.0.1: + resolution: {integrity: sha512-w7/ERvQFrGP2apTO9lDtZ+O6JQIhfakL7+Xqzh+rfMO9B4LB4qwrz+YvLgir8KFRVX64JHBnRuYBVLY1oQZcqw==} peerDependencies: expo: '*' react-native: '*' - expo-font@14.0.11: - resolution: {integrity: sha512-ga0q61ny4s/kr4k8JX9hVH69exVSIfcIc19+qZ7gt71Mqtm7xy2c6kwsPTCyhBW2Ro5yXTT8EaZOpuRi35rHbg==} + expo-font@57.0.1: + resolution: {integrity: sha512-QyS9L1Kh9sKJg4gfU6rdbpxpmH+DyzBX8z6jVvXMUDoqLr1GqmkO/Wu379KCXjL///kWbhpNlbi7AgBuj4VdIQ==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-glass-effect@0.1.8: - resolution: {integrity: sha512-9Cp17ax0Fpugue8+Bd7Ndl/dSAvGmt4bQ5mQLw9zc1A2lctUse3cEg9nI7TnDJiwKf+A/VAPN6+3K12JVMYgZg==} + expo-glass-effect@57.0.1: + resolution: {integrity: sha512-m/n8maxqNcHk6ZDhuqXBfD5Kt1Iz3M8xykVgdB0iSCIXvF70IqWXmQhX8Psswhrp8eZ+3r0mAD0Jh/2gFA3QaA==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-haptics@55.0.14: - resolution: {integrity: sha512-KjDItBsA9mi1f5nRwf8g1wOdfEcLHwvEdt5Jl1sMCDETR/homcGOl+F3QIiPOl/PRlbGVieQsjTtF4DGtHOj6g==} + expo-haptics@57.0.1: + resolution: {integrity: sha512-8VhbnxlIrfXjP0syZr1JT197nafYicQu9119adOJnX62osU9Cw+PdDnAx/6LxuKJRzQdwxOMq7b7eWjhNL5zAQ==} peerDependencies: expo: '*' - expo-image-loader@6.0.0: - resolution: {integrity: sha512-nKs/xnOGw6ACb4g26xceBD57FKLFkSwEUTDXEDF3Gtcu3MqF3ZIYd3YM+sSb1/z9AKV1dYT7rMSGVNgsveXLIQ==} + expo-image-loader@57.0.1: + resolution: {integrity: sha512-uhrZKLT/cTl2mXyR28kPpVkS5O+PK9N1QA/07IFM4f5T4g0lTW1JHT3NEWwEEsGFldPmVX4j7LwUVVZxE+woug==} peerDependencies: expo: '*' - expo-image-picker@17.0.11: - resolution: {integrity: sha512-/apkoyukDvsCHHb9fzP+F34A1uQqSzUtYH/2P/xJACNEwq+mwEXjXvVU8bzlJq6ih0Qo1+tpVivIa7B9kYSwOQ==} + expo-image-picker@57.0.6: + resolution: {integrity: sha512-6Of7SzyFVC+WFuFxhD4+nRTQ9joqldPhbiVWRsumg4ybttKmY8GxrnXAUSIDfa9DSk9PhgIAy2C1c5y1+CnU3g==} peerDependencies: expo: '*' - expo-json-utils@0.15.0: - resolution: {integrity: sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==} + expo-json-utils@57.0.1: + resolution: {integrity: sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==} - expo-keep-awake@15.0.8: - resolution: {integrity: sha512-YK9M1VrnoH1vLJiQzChZgzDvVimVoriibiDIFLbQMpjYBnvyfUeHJcin/Gx1a+XgupNXy92EQJLgI/9ZuXajYQ==} + expo-keep-awake@57.0.1: + resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 - expo-linear-gradient@15.0.8: - resolution: {integrity: sha512-V2d8Wjn0VzhPHO+rrSBtcl+Fo+jUUccdlmQ6OoL9/XQB7Qk3d9lYrqKDJyccwDxmQT10JdST3Tmf2K52NLc3kw==} + expo-linear-gradient@57.0.1: + resolution: {integrity: sha512-CpS8eMqoIWcHVGKV66zbDvzotCw9qYp3f8CuI9N+h1LaO0tMLUzBpkhAKePUsXlpN3yolYlHFSPkfVZ/uSh+iA==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-linking@8.0.11: - resolution: {integrity: sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==} + expo-linking@57.0.4: + resolution: {integrity: sha512-e1alfHNJdywIfJkCuKMc6M3hBfAGPd2gKMeF/6V7qwFWzHCS2mTBqU+KaO4FLpltA5Nt6CYEx6zmUlGfUF+8lA==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-localization@17.0.8: - resolution: {integrity: sha512-UrdwklZBDJ+t+ZszMMiE0SXZ2eJxcquCuQcl6EvGHM9K+e6YqKVRQ+w8qE+iIB3H75v2RJy6MHAaLK+Mqeo04g==} + expo-localization@57.0.1: + resolution: {integrity: sha512-8Ffl4UTbOsQeGT0v5fxMbyPHyPMPnhSPDFQJa8p9rjJrthFoAtNi+fL6Ssmrvf1/7dmPq1mVY52MEt0TMEfgjA==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 - expo-manifests@1.0.10: - resolution: {integrity: sha512-oxDUnURPcL4ZsOBY6X1DGWGuoZgVAFzp6PISWV7lPP2J0r8u1/ucuChBgpK7u1eLGFp6sDIPwXyEUCkI386XSQ==} + expo-manifests@57.0.1: + resolution: {integrity: sha512-qB/mDG2dYdl+EvUeQuqP8KFYCFgFCQjJYdWIHo8SFBgDzMYmdF286DFY2M1M9Okr99wkb5M4tgA3aCcwv3aEQA==} peerDependencies: expo: '*' - expo-modules-autolinking@3.0.24: - resolution: {integrity: sha512-TP+6HTwhL7orDvsz2VzauyQlXJcAWyU3ANsZ7JGL4DQu8XaZv/A41ZchbtAYLfozNA2Ya1Hzmhx65hXryBMjaQ==} + expo-modules-autolinking@57.0.9: + resolution: {integrity: sha512-lj2nsAKMMRLXSFnGgaaQrWJ2fdSpLPc/bca6Rkiw4g8zYPn7qX4MRUJgavvzm/hBrzvMnlhXVgJtidOGuwBh+w==} hasBin: true - expo-modules-core@3.0.29: - resolution: {integrity: sha512-LzipcjGqk8gvkrOUf7O2mejNWugPkf3lmd9GkqL9WuNyeN2fRwU0Dn77e3ZUKI3k6sI+DNwjkq4Nu9fNN9WS7Q==} + expo-modules-core@57.0.7: + resolution: {integrity: sha512-5HrbCfgYmLs0a5dzfM4GmRGelVTPIg+eYp0vmSbWnKRTOPj5DyVOfg1rHGEsuNKp07QwDxfLJfQVp8CNbuvxMQ==} + peerDependencies: + react: 19.2.6 + react-native: '*' + react-native-worklets: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + peerDependenciesMeta: + react-native-worklets: + optional: true + + expo-modules-jsi@57.0.4: + resolution: {integrity: sha512-vt7FyqUqqFXiRVnBqYD7y+GSPTgeua5Ocoy0+SYt+RSHkZEA2Fyop7If3g1TYDzQObYybPRo7TG2Rle1XLaWFw==} peerDependencies: - react: 19.1.0 react-native: '*' - expo-notifications@0.32.17: - resolution: {integrity: sha512-lwwzn7tImuzTzn9PAglZlS2VfZEvsfFGJTK9Eb8I4cqkGh2DI23YJFJH+WPEIu4QhDvk5JeBjklenJ8IZbmA4A==} + expo-notifications@57.0.7: + resolution: {integrity: sha512-77cqQ1E3B8RQ7FadKSl+bOeSzUfbMhbjMxklqQffXIHx1dILrRELFhq5/UkBW66r+F58KtGEMlw0atPVY2qJyQ==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-router@6.0.23: - resolution: {integrity: sha512-qCxVAiCrCyu0npky6azEZ6dJDMt77OmCzEbpF6RbUTlfkaCA417LvY14SBkk0xyGruSxy/7pvJOI6tuThaUVCA==} + expo-router@57.0.8: + resolution: {integrity: sha512-xAyTnZl597G9/r17GOuyTy6VlhjYCVmgzgmP00bhZ9b+VstPl3tTrOOhSFagVpeln47nKp7x7vgkANNheCv4eQ==} peerDependencies: - '@expo/metro-runtime': ^6.1.2 - '@react-navigation/drawer': ^7.5.0 - '@testing-library/react-native': '>= 12.0.0' + '@expo/log-box': ^57.0.1 + '@expo/metro-runtime': ^57.0.7 + '@testing-library/react-native': '>= 13.2.0' expo: '*' - expo-constants: ^18.0.13 - expo-linking: ^8.0.11 - react: 19.1.0 - react-dom: 19.1.0 + expo-constants: ^57.0.7 + expo-linking: ^57.0.4 + react: 19.2.6 + react-dom: 19.2.6 react-native: '*' react-native-gesture-handler: '*' react-native-reanimated: '*' react-native-safe-area-context: '>= 5.4.0' - react-native-screens: '*' + react-native-screens: ^4.26.0 react-native-web: '*' react-server-dom-webpack: ~19.0.4 || ~19.1.5 || ~19.2.4 peerDependenciesMeta: - '@react-navigation/drawer': - optional: true '@testing-library/react-native': optional: true react-dom: @@ -10448,35 +10384,44 @@ packages: react-server-dom-webpack: optional: true - expo-secure-store@15.0.8: - resolution: {integrity: sha512-lHnzvRajBu4u+P99+0GEMijQMFCOYpWRO4dWsXSuMt77+THPIGjzNvVKrGSl6mMrLsfVaKL8BpwYZLGlgA+zAw==} + expo-secure-store@57.0.1: + resolution: {integrity: sha512-tLa1VmSadOq19mA/dwkl99RbHyjLE0T1qqBYMY3/OsguZTI+rlrDy/DDJjupqlVtmr95hD7o1pYqx5aL+B4YMA==} peerDependencies: expo: '*' - expo-server@1.0.5: - resolution: {integrity: sha512-IGR++flYH70rhLyeXF0Phle56/k4cee87WeQ4mamS+MkVAVP+dDlOHf2nN06Z9Y2KhU0Gp1k+y61KkghF7HdhA==} + expo-server@57.0.1: + resolution: {integrity: sha512-sBfVDH6dmKVHZxqUxbfkzS00PZELMZt1IpnHKxcOTMZtR/t7CtRAFrbXcisG+EyzeqHSVDacZT+1tbYfZt5D8w==} engines: {node: '>=20.16.0'} - expo-speech-recognition@3.1.2: - resolution: {integrity: sha512-yaXy+6w218Urdshits2KsfLjXNCnGNlXzUxEP4BVehKEbiIPAeUKBzuicCeELU5H2zTLwL9u+RjbFAUom4LiYQ==} + expo-speech-recognition@56.0.1: + resolution: {integrity: sha512-TpP1KCiq3vYfSQF0XpUkWLXp4mTw6MLuyMGPzVVKwqzs7oiKlJ/e0q3PNNjfqs058X47ftqT+31lTaWRWRbmPg==} peerDependencies: expo: '*' - react: 19.1.0 + react: 19.2.6 react-native: '*' - expo-splash-screen@31.0.13: - resolution: {integrity: sha512-1epJLC1cDlwwj089R2h8cxaU5uk4ONVAC+vzGiTZH4YARQhL4Stlz1MbR6yAS173GMosvkE6CAeihR7oIbCkDA==} + expo-splash-screen@57.0.5: + resolution: {integrity: sha512-ZN0LDXlhHRNFjXTYZDojXk8IfaoUIu7qa3hhoBTXgyj1UB/iewGlH6+M3Nvhun2lY2d/+xhwqMhv0hIRoBo09Q==} + peerDependencies: + expo: '*' + + expo-status-bar@57.0.1: + resolution: {integrity: sha512-Xwaq1gAoVRWx5dPG5VhT5RSbnI9OilhZnO5qoPBnUaBAa5VzRzfdS8q0/bsPt0jR2DKLtGuP0bQ6efMJ4RIMDg==} peerDependencies: expo: '*' + react: 19.2.6 + react-native: '*' - expo-status-bar@3.0.9: - resolution: {integrity: sha512-xyYyVg6V1/SSOZWh4Ni3U129XHCnFHBTcUo0dhWtFDrZbNp/duw5AGsQfb2sVeU0gxWHXSY1+5F0jnKYC7WuOw==} + expo-symbols@57.0.1: + resolution: {integrity: sha512-8Zf+a83OywV0vf1NUtSKpNqKcULmO0GTI+zfFnGYl7SLDH9FjL5RcEZoy6CHvCgq2KDrQF21pl3r7Tb4ItPscw==} peerDependencies: - react: 19.1.0 + expo: '*' + expo-font: '*' + react: 19.2.6 react-native: '*' - expo-system-ui@6.0.9: - resolution: {integrity: sha512-eQTYGzw1V4RYiYHL9xDLYID3Wsec2aZS+ypEssmF64D38aDrqbDgz1a2MSlHLQp2jHXSs3FvojhZ9FVela1Zcg==} + expo-system-ui@57.0.1: + resolution: {integrity: sha512-r8a6Jk2suL0vI7Uq4iKJab5Eesk8dkB56Q6HksVNkzuAExV0axoikQwZv8aAyHGbu2VHp0artB0N1/PQDLSgBg==} peerDependencies: expo: '*' react-native: '*' @@ -10485,31 +10430,37 @@ packages: react-native-web: optional: true - expo-updates-interface@2.0.0: - resolution: {integrity: sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==} + expo-updates-interface@57.0.1: + resolution: {integrity: sha512-+LUWwJ0gf/TEKMVdQAw/Gjih4dvrk+URgy24X9qEGKuuMDZqjBRm9T4yQyBVALGL5TTdPUaB6ILxx3lshm3pwQ==} peerDependencies: expo: '*' - expo-web-browser@15.0.10: - resolution: {integrity: sha512-fvDhW4bhmXAeWFNFiInmsGCK83PAqAcQaFyp/3pE/jbdKmFKoRCWr46uZGIfN4msLK/OODhaQ/+US7GSJNDHJg==} + expo-web-browser@57.0.2: + resolution: {integrity: sha512-3vl5kvd7PB48ub6PpNIJUuPxO8xVa6D8RnIgNba6SXRwqFprOfeEZgwTgtm41kz0AAtvMOztUVNEUkwrHKjqMQ==} peerDependencies: expo: '*' react-native: '*' - expo@54.0.33: - resolution: {integrity: sha512-3yOEfAKqo+gqHcV8vKcnq0uA5zxlohnhA3fu4G43likN8ct5ZZ3LjAh9wDdKteEkoad3tFPvwxmXW711S5OHUw==} + expo@57.0.8: + resolution: {integrity: sha512-0IxxoPZbT54IH4fHL5NihkvED9HBVQx3uNdPvyv8pFUHWJ81RdFjL0aJys1IB7hbo09KTz4xW4I2aWVOXKAcJQ==} hasBin: true peerDependencies: '@expo/dom-webview': '*' '@expo/metro-runtime': '*' - react: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-native: '*' + react-native-web: '*' react-native-webview: '*' peerDependenciesMeta: '@expo/dom-webview': optional: true '@expo/metro-runtime': optional: true + react-dom: + optional: true + react-native-web: + optional: true react-native-webview: optional: true @@ -10555,6 +10506,11 @@ packages: fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fb-dotslash@0.5.8: + resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} + engines: {node: '>=20'} + hasBin: true + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -10580,6 +10536,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fetch-nodeshim@0.4.10: + resolution: {integrity: sha512-m6I8ALe4L4XpdETy7MJZWs6L1IVMbjs99bwbpIKphxX+0CTns4IKDWJY0LWfr4YsFjfg+z1TjzTMU8lKl8rG0w==} + fflate@0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} @@ -10676,10 +10635,6 @@ packages: fontfaceobserver@2.3.0: resolution: {integrity: sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@2.0.0: resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} engines: {node: '>=8.0.0'} @@ -10709,8 +10664,8 @@ packages: resolution: {integrity: sha512-Tnd0FU05zGRFI3JJmBegXonF1rfuzYeuXd1QSdQ99Ysnppk0yWBWSW2wUsqzRpS5nv0zPNx+y0wtDj4kf0q5RQ==} peerDependencies: '@emotion/is-prop-valid': '*' - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@emotion/is-prop-valid': optional: true @@ -10719,10 +10674,6 @@ packages: react-dom: optional: true - freeport-async@2.0.0: - resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==} - engines: {node: '>=8'} - fresh@0.5.2: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} @@ -10795,10 +10746,6 @@ packages: resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -10907,10 +10854,6 @@ packages: resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} engines: {node: '>=10.0'} - global-dirs@0.1.1: - resolution: {integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==} - engines: {node: '>=4'} - global-modules@0.2.3: resolution: {integrity: sha512-JeXuCbvYzYXcwE6acL9V2bAOeSIGl4dD+iwLY9iUx2VBJJ80R18HCn+JCwHM9Oegdfya3lEkGCdaRkSyc10hDA==} engines: {node: '>=0.10.0'} @@ -11021,23 +10964,32 @@ packages: headers-polyfill@4.0.3: resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hermes-compiler@250829098.0.14: + resolution: {integrity: sha512-5meXwsZxgiqFaJjNzwjzI9IyUkuGGBisu+z9BvQWmGVpjH6nz11hgqkyxe4dl8UAdyIV4lTbz91+Dlnjz0VxqA==} + hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - hermes-estree@0.29.1: - resolution: {integrity: sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==} + hermes-estree@0.35.0: + resolution: {integrity: sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==} - hermes-estree@0.32.0: - resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} + hermes-estree@0.36.0: + resolution: {integrity: sha512-A1+8zn5oss2CFP7pKsOaxorQG6FNIz1WU1VDqruLPPZl3LVgeE2C5xfFg8Ow6/Ow4mSslLLtYP1J3n38eKyW9w==} + + hermes-estree@0.36.1: + resolution: {integrity: sha512-guv1nQ6IJ7S83NRFPWc3SA7IBZrdNC9kapwOq6uXvF4wP+sDCgjzQbKPCoyYmoyZRzztF/n/c36l/rccCZSiCw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hermes-parser@0.29.1: - resolution: {integrity: sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==} + hermes-parser@0.35.0: + resolution: {integrity: sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==} + + hermes-parser@0.36.0: + resolution: {integrity: sha512-GdpwMmH5x6IpC1cijvcvYnlPB60Mh6kTSF/NFdYV/j56gYdi+0RIakYs+eqOV+bbO0SW7mgVVGSsTJxyPQfo3w==} - hermes-parser@0.32.0: - resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==} + hermes-parser@0.36.1: + resolution: {integrity: sha512-GApNk4zLHi2UWoWZZkx7LNCOSzLSc5lB55pZ/PhK7ycFeg7u5LcF88p/WbpIi1XUDtE0MpHE3uRR3u3KB7TjSQ==} highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -11192,6 +11144,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. @@ -11202,6 +11158,26 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ink-spinner@5.0.0: + resolution: {integrity: sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==} + engines: {node: '>=14.16'} + peerDependencies: + ink: '>=4.0.0' + react: 19.2.6 + + ink@7.1.1: + resolution: {integrity: sha512-Y43xxa1ZSPvpmfLHcN5o+OdP8Rf8ykkNJEuKYOUNZKT8wXVNLFTtEm1nSDMQkfBH+YANF4Xuu0hhZ4ejqAtN2w==} + engines: {node: '>=22'} + peerDependencies: + '@types/react': ^19.2.15 + react: 19.2.6 + react-devtools-core: '>=6.1.2' + peerDependenciesMeta: + '@types/react': + optional: true + react-devtools-core: + optional: true + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -11232,10 +11208,6 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-arguments@1.2.0: - resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} - engines: {node: '>= 0.4'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -11246,10 +11218,6 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - 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'} @@ -11287,10 +11255,6 @@ packages: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -11298,6 +11262,11 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-in-ci@2.0.0: + resolution: {integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==} + engines: {node: '>=20'} + hasBin: true + is-in-ssh@1.0.0: resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} engines: {node: '>=20'} @@ -11311,10 +11280,6 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} - is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} - is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} @@ -11340,10 +11305,6 @@ packages: 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'} @@ -11363,10 +11324,6 @@ packages: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -11439,10 +11396,6 @@ packages: resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==} engines: {node: '>=8'} - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} @@ -11520,10 +11473,6 @@ packages: resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-environment-node@30.4.1: resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -11532,10 +11481,6 @@ packages: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-haste-map@30.4.1: resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -11561,18 +11506,10 @@ packages: resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-message-util@30.4.1: resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-mock@30.4.1: resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -11590,10 +11527,6 @@ packages: resolution: {integrity: sha512-80Y6snDyb0p8GG83pDxGI/kQzwVTkCxc7ep5FPe/F6JYdvRDhwr6RzRmPSP7SEwuLhxo80lBS/NqOdUIbHIfhw==} deprecated: ⚠️ The 'jest-process-manager' package is deprecated. Please migrate to Playwright's built-in test runner (@playwright/test) which now includes full Jest-style features and parallel testing. See https://playwright.dev/docs/intro for details. - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-regex-util@30.4.0: resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -11806,8 +11739,8 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4 <7' - lan-network@0.1.7: - resolution: {integrity: sha512-mnIlAEMu4OyEvUNdzco9xpuB9YVcPkQec+QsgycBCtPZvEqWPCDPfbAE4OJMdBBWpZWtpCn1xw9jJYlwjWI5zQ==} + lan-network@0.2.1: + resolution: {integrity: sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A==} hasBin: true launch-editor@2.14.1: @@ -11827,12 +11760,6 @@ packages: lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -11845,12 +11772,6 @@ packages: cpu: [arm64] os: [darwin] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} @@ -11863,12 +11784,6 @@ packages: cpu: [x64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} @@ -11881,12 +11796,6 @@ packages: cpu: [x64] os: [freebsd] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} @@ -11899,12 +11808,6 @@ packages: cpu: [arm] os: [linux] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} @@ -11918,13 +11821,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -11939,13 +11835,6 @@ packages: os: [linux] libc: [musl] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -11960,13 +11849,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -11981,13 +11863,6 @@ packages: os: [linux] libc: [musl] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -12001,12 +11876,6 @@ packages: cpu: [arm64] os: [win32] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} @@ -12019,12 +11888,6 @@ packages: cpu: [x64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} @@ -12035,10 +11898,6 @@ packages: resolution: {integrity: sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==} engines: {node: '>= 12.0.0'} - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} - engines: {node: '>= 12.0.0'} - lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} @@ -12181,12 +12040,12 @@ packages: lucide-react@0.577.0: resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} peerDependencies: - react: 19.1.0 + react: 19.2.6 lucide-react@1.7.0: resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} @@ -12326,62 +12185,62 @@ packages: resolution: {integrity: sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ==} engines: {node: '>=18.0.0'} - metro-babel-transformer@0.83.3: - resolution: {integrity: sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==} - engines: {node: '>=20.19.4'} + metro-babel-transformer@0.84.4: + resolution: {integrity: sha512-rvCfz8snl9h20VcvpOHxZuHP1SlAkv4HXbzw7nyyVwu6Eqo5PRerbakQ9XmUCOsRy70spJ37O+G1TK8oMzo48g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache-key@0.83.3: - resolution: {integrity: sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==} - engines: {node: '>=20.19.4'} + metro-cache-key@0.84.4: + resolution: {integrity: sha512-wVO79aGrkYImpnaVS4+d5RrRBRPX31QtvKB3wKGBuiNSznduZTQHzsrJZRroFJSwnygrzdsGUtDQPuqqFjFdvw==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-cache@0.83.3: - resolution: {integrity: sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==} - engines: {node: '>=20.19.4'} + metro-cache@0.84.4: + resolution: {integrity: sha512-gpcFQdSLUwUCk71saKoE64jLFbx2nwTfVCcPSULMNT8QYq0p1eZZE29Jvd0HtT/UlhC3ZOutLxJME5xqD2JUZg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-config@0.83.3: - resolution: {integrity: sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==} - engines: {node: '>=20.19.4'} + metro-config@0.84.4: + resolution: {integrity: sha512-PMotGDjXcXLWo2TMRH+VR99phFNgYTwqh4OoieIKK3yTJa1Jmkl+fZJxDO0jfBvNF+WESHciHvpNuBtXaF3B0Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-core@0.83.3: - resolution: {integrity: sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==} - engines: {node: '>=20.19.4'} + metro-core@0.84.4: + resolution: {integrity: sha512-HONpWC5LGXZn3ffkd4Hu6AIrfE7j4Z0g0wMo/goV24WOB3lhuFZ40KgvaDiSw8iyQHloMYay5N/wPX+z8oN/PQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-file-map@0.83.3: - resolution: {integrity: sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==} - engines: {node: '>=20.19.4'} + metro-file-map@0.84.4: + resolution: {integrity: sha512-KSVDi/u60hKPx++NLu3MTIvyjzNoJnFAF8PQFxaj1jiSka/wjw+Ua6sNuJ0TDHQv+7AAoFQxeMgaRAe8Yic5wQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-minify-terser@0.83.3: - resolution: {integrity: sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==} - engines: {node: '>=20.19.4'} + metro-minify-terser@0.84.4: + resolution: {integrity: sha512-5qpbaVOMC7CPitIpuewzVeGw7E+C3ykbv2mqTjQLl85Z3annSVGlSCTcsZjqXZzjupfK4Ztj3dDc4kc44NZwtQ==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-resolver@0.83.3: - resolution: {integrity: sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==} - engines: {node: '>=20.19.4'} + metro-resolver@0.84.4: + resolution: {integrity: sha512-1qLgbxQ5ZGhhutuPot1Yp348ofDsATL2WkrHF65TobqTT9K3P9qJXw38bomk7ncp5B7OYMfWwtyBZo1lCV792A==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-runtime@0.83.3: - resolution: {integrity: sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==} - engines: {node: '>=20.19.4'} + metro-runtime@0.84.4: + resolution: {integrity: sha512-Jibypds4g7AhzdRKY+kDoj51s5EXMwgyp5ddtlreDAsWefMdOx+agWqgm0H2XSZ/ueanHHVM89fnf5OJnlxa8Q==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-source-map@0.83.3: - resolution: {integrity: sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==} - engines: {node: '>=20.19.4'} + metro-source-map@0.84.4: + resolution: {integrity: sha512-jbWkPxIesVuo1IWkvezmMJld6iu8nD62GsrZiV6jP37AOdbo4OBq1FJ+qkOg8sV05wAHB//jAbziuW0SlJfW4g==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-symbolicate@0.83.3: - resolution: {integrity: sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==} - engines: {node: '>=20.19.4'} + metro-symbolicate@0.84.4: + resolution: {integrity: sha512-OnfpacxUqGPZQ27t8qK9mFa7uqHIlVWeqRqkCbvMvreEBiamEeOn8krKtcwgP5M4cYDPwuSmCTopHMVthqG4zA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true - metro-transform-plugins@0.83.3: - resolution: {integrity: sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==} - engines: {node: '>=20.19.4'} + metro-transform-plugins@0.84.4: + resolution: {integrity: sha512-kehr6HbAecqD0/a3xLXobELdPaAmRAl8bel0qagPF4vhZtux93nS8S4eq2kgKt6J2GnQpVjSoW1PXdst04mwow==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro-transform-worker@0.83.3: - resolution: {integrity: sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==} - engines: {node: '>=20.19.4'} + metro-transform-worker@0.84.4: + resolution: {integrity: sha512-W1IYMvvXTu4MxYr7d9h7CeG2vpIr3bmLLIavkPY4O1ilzDrvS8z/NEe6y+pC44Ff7raMXQgYSfdqDUwN/i39gg==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} - metro@0.83.3: - resolution: {integrity: sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==} - engines: {node: '>=20.19.4'} + metro@0.84.4: + resolution: {integrity: sha512-8ETTubqfD6ornDy2zYDvRcKnVDOXdFJsjetYDBsY4oAsb6NJkiwFR+FaMESyGppFmQUyBQA4H4sFGxzcQSGtFA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true micromark-core-commonmark@2.0.3: @@ -12621,6 +12480,9 @@ packages: typescript: optional: true + multitars@1.0.0: + resolution: {integrity: sha512-H/J4fMLedtudftaYMOg7ajzLYgT3/rwbWVJbqr/iUgB8DQztn38ys5HOqI1CzSxx8QhXXwOOnnBvd4v3jG5+Mg==} + mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -12628,11 +12490,6 @@ packages: 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.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -12670,9 +12527,6 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - nested-error-stacks@2.0.1: - resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==} - no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} @@ -12776,9 +12630,9 @@ packages: engines: {node: '>=8.9'} hasBin: true - ob1@0.83.3: - resolution: {integrity: sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==} - engines: {node: '>=20.19.4'} + ob1@0.84.4: + resolution: {integrity: sha512-eJXMpz4aQHXF/YBB9ddqZDIS+ooO91hObo9FoW/xBkr54/zCwYYCDqT/O54vNo8kOkWs5Ou/y28NgdrV0edQNA==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -12792,10 +12646,6 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -12804,10 +12654,6 @@ packages: resolution: {integrity: sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A==} engines: {node: '>= 10'} - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -12863,10 +12709,6 @@ packages: resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} engines: {node: '>=8'} - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - openai@6.26.0: resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==} hasBin: true @@ -12923,8 +12765,8 @@ packages: resolution: {integrity: sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-parser@0.138.0: - resolution: {integrity: sha512-c25lvfpZ2+WY1yk6NkP0X0RTQg0ZxgSVaZHDa7lt6fEe1jwZjPWkRWvTyZ1xyaM7roVJMdtRCfbhUj/d4ims3Q==} + oxc-parser@0.141.0: + resolution: {integrity: sha512-uFkGGr1KMWd6aWv9UAqooYrN78trw8MWWmoPvgWokfBEUq1+eiIQ+qfj3wokhy0fxtZWZk+0dHoS7/yRTJtd6w==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.17.0: @@ -12941,19 +12783,22 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint-plugin-react-doctor@0.8.3: - resolution: {integrity: sha512-S1Gq1H9+BpziApWcZ/sWPNYYy1FMkFmtSEGiqIJ4/Aac76LfkeutPFtSRlkJF1JlcrbYFf7LXcfcxZCJgmVBBQ==} + oxlint-plugin-react-doctor@0.9.2: + resolution: {integrity: sha512-fTciSOgAGe/KAgvFDKLz9crjxHyAuu0BvT5sXfSdo2B5QmY5Y/j3nliqX6FaGr7Wud1SYvkAky+/m+58b6K6fQ==} engines: {node: ^20.19.0 || >=22.13.0} - oxlint@1.66.0: - resolution: {integrity: sha512-N4LLxYLd94KEBqXDMDM5f+2PUpItTjDLreXe2Gn5KhjhCK4Qp2YUXaBi8Yu325ryOgKwt22m45fpD7nPOn69Yw==} + oxlint@1.74.0: + resolution: {integrity: sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.22.1' + oxlint-tsgolint: '>=0.24.0' + vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: optional: true + vite-plus: + optional: true p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} @@ -13060,7 +12905,7 @@ packages: resolution: {integrity: sha512-K4ClMxRKpgN4sXj6VIPPrvor/TMp2yPNCGtfhvV106C73SwefQ3FuegURsH7AQHpqu0WwbvKXRl1HQxF6qax9w==} engines: {node: '>=14.x'} peerDependencies: - react: 19.1.0 + react: 19.2.6 xstate: '>=4.32.1' peerDependenciesMeta: react: @@ -13068,6 +12913,10 @@ packages: xstate: optional: true + patch-console@2.0.0: + resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -13125,7 +12974,7 @@ packages: phosphor-react-native@3.0.3: resolution: {integrity: sha512-h8UIIG/V4pgm20uvkt7L8G/GsOWKaU7rnyu2jnGt1vKmaigE0GZWXOHoEw9wZcfATHwvUpr/mkubEG/nbKeJkg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' react-native-svg: '*' @@ -13136,14 +12985,14 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@3.0.1: - resolution: {integrity: sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==} - engines: {node: '>=10'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -13209,10 +13058,6 @@ packages: resolution: {integrity: sha512-rEwq/ZHlJIKw++XtLAO8PPuOQA/zaPJOZJ37BVuN97nLpMJeuDVLVGRwbFoBgLudgdTMP2hdRJP++H+8QOA3vg==} engines: {node: '>= 10.12'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -13260,10 +13105,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.15: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} @@ -13283,7 +13124,7 @@ packages: posthog-react-native-session-replay@1.6.0: resolution: {integrity: sha512-OCaei77mtgg7JT+TgHSCgpWeKq2XXENUOPNxGbjhXZa/aJpptOW5VsBqjtH4BPzM2c1veS1DK4/Fb/uV4Rb3cg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' posthog-react-native@4.30.0: @@ -13360,10 +13201,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-bytes@5.6.0: - resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} - engines: {node: '>=6'} - pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -13516,10 +13353,6 @@ packages: resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} engines: {node: '>=16.0.0'} - qrcode-terminal@0.11.0: - resolution: {integrity: sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==} - hasBin: true - qs@6.15.0: resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} @@ -13549,8 +13382,8 @@ packages: peerDependencies: '@types/react': ^19.2.15 '@types/react-dom': ^19.2.3 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -13584,20 +13417,15 @@ packages: resolution: {integrity: sha512-+NRMYs2DyTP4/tqWz371Oo50JqmWltR1h2gcdgUMAWZJIAvrd0/SqlCfx7tpzpl/s36rzw6qH2MjoNrxtRNYhA==} engines: {node: ^20.9.0 || >=22} - react-doctor@0.8.3: - resolution: {integrity: sha512-FfG7YQKb1yv1UNk2gknZzLAPx6L3RMOhYsYbslr4MSpVMNk5xBHlu9VxjtS0br0DEBWjkZovrf/C1MrchiIzAw==} + react-doctor@0.9.2: + resolution: {integrity: sha512-A/e21t0y3j7zUTS8lJyNI2pKMfYLDH+zZwqAC7+MQW1vCD3xqWc2x8XCCWKnrQQwtFd6dwSZw2017x1iSLnGTA==} engines: {node: ^20.19.0 || >=22.13.0} hasBin: true - react-dom@19.1.0: - resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} - peerDependencies: - react: 19.1.0 - react-dom@19.2.6: resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -13606,13 +13434,13 @@ packages: resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==} engines: {node: '>=10'} peerDependencies: - react: 19.1.0 + react: 19.2.6 - react-grab@0.1.48: - resolution: {integrity: sha512-p3WnmK9LLvXE/c4ITPLlXcP1fkXo2VFEQqK94tIfcHIWKNdqdhYYFyNVioO50HR+uyHIwT63Z4txZDqJlVcD/Q==} + react-grab@0.1.50: + resolution: {integrity: sha512-zRkHKq/8a1msCpEOp8BDROeQZT50m0OH2XPrP6jk5op+JAHrlsm3pj7eAQMOsct87EZDeGNnu4r+sGsJJzyw1Q==} hasBin: true peerDependencies: - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: react: optional: true @@ -13620,8 +13448,8 @@ packages: react-hotkeys-hook@4.6.2: resolution: {integrity: sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -13639,13 +13467,13 @@ packages: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 react-native-css-interop@0.2.1: resolution: {integrity: sha512-B88f5rIymJXmy1sNC/MhTkb3xxBej1KkuAt7TiT9iM7oXz3RM8Bn+7GUrfR02TvSgKm4cg2XiSuLEKYfKwNsjA==} engines: {node: '>=18'} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' react-native-reanimated: '>=3.6.2' react-native-safe-area-context: '*' @@ -13657,37 +13485,56 @@ packages: react-native-svg: optional: true - react-native-is-edge-to-edge@1.2.1: - resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==} + react-native-drawer-layout@4.2.9: + resolution: {integrity: sha512-ETOxvlhhb4LmuuG3RN7A3qwt9jr9AZ2it+1G2kNE4g2fTyxxay7QQkPm1HfKi/JzmMSWC5+YTepGSgZNpJIDGg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' + react-native-gesture-handler: '>= 2.0.0' + react-native-reanimated: '>= 2.0.0' - react-native-keyboard-controller@1.18.5: - resolution: {integrity: sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ==} + react-native-gesture-handler@3.1.0: + resolution: {integrity: sha512-+kWVyZ6vLdtyFa1/aOc/sZncLAVPUKxhji/ttbiLI7hOaSU44bLjhI7p+Ns+pMl2r3RqPXssl5qHReYet9G77g==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' - react-native-reanimated: '>=3.0.0' - react-native-reanimated@4.1.6: - resolution: {integrity: sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ==} + react-native-is-edge-to-edge@1.2.1: + resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==} peerDependencies: - '@babel/core': ^7.0.0-0 - react: 19.1.0 + react: 19.2.6 react-native: '*' - react-native-worklets: '>=0.5.0' - react-native-safe-area-context@5.6.2: - resolution: {integrity: sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==} + react-native-is-edge-to-edge@1.3.1: + resolution: {integrity: sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' - react-native-screens@4.16.0: - resolution: {integrity: sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==} + react-native-keyboard-controller@1.21.9: + resolution: {integrity: sha512-+TkkFldht4+AXBQeDy1hLE7iqiW8/NkY/ekhcFsKIiRdI9qC5JDzx0TfAg1iYZB2IeOXppmURIy2jFCUjOcV1w==} peerDependencies: - react: 19.1.0 + react: 19.2.6 + react-native: '*' + react-native-reanimated: '>=3.0.0' + + react-native-reanimated@4.5.0: + resolution: {integrity: sha512-+iPfvK34PKKYP/p/4TaBliFkbfvjGDIvXuiiaxvISP5ip7sWegvlacwU/uAV6zNDSSmX0tDyER7PurPMKGDipA==} + peerDependencies: + react: 19.2.6 + react-native: 0.83 - 0.86 + react-native-worklets: 0.10.x + + react-native-safe-area-context@5.7.0: + resolution: {integrity: sha512-/9/MtQz8ODphjsLdZ+GZAIcC/RtoqW9EeShf7Uvnfgm/pzYrJ75y3PV/J1wuAV1T5Dye5ygq4EAW20RoBq0ABQ==} + peerDependencies: + react: 19.2.6 + react-native: '*' + + react-native-screens@4.26.2: + resolution: {integrity: sha512-2XnWsZToKj76trGtEZzx5ELD/qOICFEprEeUntImmitQFVUkea27fiWdUSITArI356Y1qynpXZINW+Unbhky/A==} + peerDependencies: + react: 19.2.6 react-native: '*' react-native-svg-transformer@1.5.3: @@ -13696,42 +13543,52 @@ packages: react-native: '>=0.59.0' react-native-svg: '>=12.0.0' - react-native-svg@15.15.2: - resolution: {integrity: sha512-lpaSwA2i+eLvcEdDZyGgMEInQW99K06zjJqfMFblE0yxI0SCN5E4x6in46f0IYi6i3w2t2aaq3oOnyYBe+bo4w==} + react-native-svg@15.15.4: + resolution: {integrity: sha512-boT/vIRgj6zZKBpfTPJJiYWMbZE9duBMOwPK6kCSTgxsS947IFMOq9OgIFkpWZTB7t229H24pDRkh3W9ZK/J1A==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' react-native-web@0.21.2: resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 - react-native-webview@13.16.0: - resolution: {integrity: sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==} + react-native-webview@13.16.1: + resolution: {integrity: sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-native: '*' - react-native-worklets@0.7.2: - resolution: {integrity: sha512-DuLu1kMV/Uyl9pQHp3hehAlThoLw7Yk2FwRTpzASOmI+cd4845FWn3m2bk9MnjUw8FBRIyhwLqYm2AJaXDXsog==} + react-native-worklets@0.10.0: + resolution: {integrity: sha512-JhE6IxDf6iabC0qu3+TAKA4v9RlluXmoIngPQX7/QUByf75lfrsHZ6/dQhyjEWnp1EEQiwzz8Cpew140ZcewDw==} peerDependencies: '@babel/core': '*' - react: 19.1.0 - react-native: '*' + '@react-native/metro-config': '*' + react: 19.2.6 + react-native: 0.83 - 0.86 - react-native@0.81.5: - resolution: {integrity: sha512-1w+/oSjEXZjMqsIvmkCRsOc8UBYv163bTWKTI8+1mxztvQPhCRYGTvZ/PL1w16xXHneIj/SLGfxWg2GWN2uexw==} - engines: {node: '>= 20.19.4'} + react-native@0.86.0: + resolution: {integrity: sha512-17ALh/dd6AO4pgOVmOO5Axll5PbErEo3XFyLokyzW6usyi+OShIEPwUW26wLPlhVifgSOIfECCH0WN+0IqtJ1w==} + engines: {node: ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0} hasBin: true peerDependencies: + '@react-native/jest-preset': 0.86.0 '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: + '@react-native/jest-preset': + optional: true '@types/react': optional: true + react-reconciler@0.33.0: + resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: 19.2.6 + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} @@ -13749,7 +13606,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -13759,7 +13616,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -13767,22 +13624,22 @@ packages: react-resizable-panels@3.0.6: resolution: {integrity: sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-resizable-panels@4.10.0: resolution: {integrity: sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-scan@0.5.7: resolution: {integrity: sha512-KRlq734yN6q/f2CZmZi9CWHuiqSzoLhPFLtcJOL6XM4lR54myyFcY81pG9QOwj+eBC1hIHm5n+Ntbtqiilu8Rg==} hasBin: true peerDependencies: esbuild: '>=0.18.0' - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 peerDependenciesMeta: esbuild: optional: true @@ -13791,34 +13648,30 @@ packages: resolution: {integrity: sha512-kY+w4OMNZ8Nj9YI9eiTgvvJ/wYO7XyX1D/LYhvwQZv5vw69iCiDtGB0BX/2U8gLUuZAMN+x/7rHJKqHh8wXFHQ==} peerDependencies: prop-types: ^15.0.0 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true - react-test-renderer@19.1.0: - resolution: {integrity: sha512-jXkSl3CpvPYEF+p/eGDLB4sPoDX8pKkYvRl9+rR8HxLY0X04vW7hCm1/0zHoUSjPZ3bDa+wXWNTDVIw/R8aDVw==} + react-test-renderer@19.2.6: + resolution: {integrity: sha512-GbS6V23YduFTPiWJ5xICbKEjRcqx1Z90js/V5miqhz7qp/d6xSe9Dd6NjSQODFRdzdsqRMPW82E/sFpPRbY5Mw==} peerDependencies: - react: 19.1.0 + react: 19.2.6 react-zoom-pan-pinch@4.0.3: resolution: {integrity: sha512-N2Hi6L78fFmhRra+ORpFSW7WST5x6kxpOPplIvtB0b7b+U2anpo1z1wLgaWRPS2kUSqcraRG+JgBCIlDJnqqAg==} engines: {node: '>=8', npm: '>=5'} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 - - react@19.1.0: - resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} - engines: {node: '>=0.10.0'} + react: 19.2.6 + react-dom: 19.2.6 react@19.2.6: resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} @@ -13928,10 +13781,6 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - requireg@0.2.2: - resolution: {integrity: sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==} - engines: {node: '>= 4.0.0'} - requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -13961,28 +13810,17 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-global@1.0.0: - resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==} - engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} resolve-workspace-root@2.0.1: resolution: {integrity: sha512-nR23LHAvaI6aHtMg6RWoaHpdR4D881Nydkzi2CixINyg9T00KgaJdJI6Vwty+Ps8WLxZHuxsS0BseWjxSA4C+w==} - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true - resolve@1.7.1: - resolution: {integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==} - responselike@2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} @@ -13990,6 +13828,10 @@ packages: resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} engines: {node: '>=4'} + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -14111,10 +13953,6 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -14129,9 +13967,6 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} - scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -14156,16 +13991,6 @@ packages: 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.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -14224,10 +14049,6 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} @@ -14253,10 +14074,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.3: - resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} - engines: {node: '>= 0.4'} - shell-quote@1.9.0: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} @@ -14339,6 +14156,10 @@ packages: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} + slice-ansi@9.0.0: + resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} + engines: {node: '>=22'} + slugify@1.6.6: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} @@ -14416,6 +14237,9 @@ packages: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} + standard-navigation@0.0.5: + resolution: {integrity: sha512-YAmzwAiiQVocZxO/VGPFiQHcu5pKiz09QIGC0MK6aRMoa3E0QkoTQgcqJr7ZZ3OMiNhu4DkaGElFI5htjOIDbw==} + standardwebhooks@1.0.0: resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} @@ -14683,10 +14507,6 @@ packages: engines: {node: '>=18'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - temp-dir@2.0.0: - resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} - engines: {node: '>=8'} - temp-file@3.4.0: resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} @@ -14698,6 +14518,10 @@ packages: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} engines: {node: '>=8'} + terminal-size@4.0.1: + resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} + engines: {node: '>=18'} + terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} @@ -14828,6 +14652,9 @@ packages: resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} + toqr@0.1.1: + resolution: {integrity: sha512-FWAPzCIHZHnrE/5/w9MPk0kK25hSQSH2IKhYh9PyjS3SG/+IEMvlwIHbhz+oF7xl54I+ueZlVnMjyzdSwLmAwA==} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -15039,10 +14866,6 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici@6.23.0: - resolution: {integrity: sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==} - engines: {node: '>=18.17'} - undici@7.27.2: resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} engines: {node: '>=20.18.1'} @@ -15082,10 +14905,6 @@ packages: resolution: {integrity: sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==} engines: {node: '>= 0.8.0'} - unique-string@2.0.0: - resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} - engines: {node: '>=8'} - unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} @@ -15147,7 +14966,7 @@ packages: engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -15155,14 +14974,14 @@ packages: use-latest-callback@0.2.6: resolution: {integrity: sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==} peerDependencies: - react: 19.1.0 + react: 19.2.6 use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} peerDependencies: '@types/react': ^19.2.15 - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -15170,7 +14989,7 @@ packages: use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: - react: 19.1.0 + react: 19.2.6 utf8-byte-length@1.0.5: resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} @@ -15181,9 +15000,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -15225,8 +15041,8 @@ packages: vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -15240,8 +15056,8 @@ packages: virtua@0.48.6: resolution: {integrity: sha512-Cl4uMvMV5c9RuOy9zhkFMYwx/V4YLBMYLRSWkO8J46opQZ3P7KMq0CqCVOOAKUckjl/r//D2jWTBGYWzmgtzrQ==} peerDependencies: - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 solid-js: '>=1.0' svelte: '>=5.0' vue: '>=3.2' @@ -15463,10 +15279,6 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@5.0.0: - resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} - engines: {node: '>=8'} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -15505,9 +15317,8 @@ packages: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} - whatwg-url-without-unicode@8.0.0-3: - resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==} - engines: {node: '>=10'} + whatwg-url-minimum@0.1.2: + resolution: {integrity: sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A==} whatwg-url@14.2.0: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} @@ -15522,10 +15333,6 @@ packages: which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} - engines: {node: '>= 0.4'} - which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -15555,13 +15362,18 @@ packages: engines: {node: '>=8'} hasBin: true - wonka@6.3.5: - resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==} + widest-line@6.0.0: + resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} + engines: {node: '>=20'} word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -15584,16 +15396,13 @@ packages: write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - write-file-atomic@5.0.1: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@6.2.3: - resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -15603,20 +15412,20 @@ packages: utf-8-validate: optional: true - ws@7.5.10: - resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} - engines: {node: '>=8.3.0'} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -15722,6 +15531,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + yoga-wasm-web@0.3.3: resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} @@ -15748,7 +15560,7 @@ packages: peerDependencies: '@types/react': ^19.2.15 immer: '>=9.0.6' - react: 19.1.0 + react: 19.2.6 peerDependenciesMeta: '@types/react': optional: true @@ -15767,10 +15579,6 @@ packages: snapshots: - '@0no-co/graphql.web@1.2.0(graphql@16.12.0)': - optionalDependencies: - graphql: 16.12.0 - '@adobe/css-tools@4.4.4': {} '@agentclientprotocol/sdk@0.19.0(zod@4.4.3)': @@ -15785,6 +15593,11 @@ snapshots: dependencies: zod: 4.4.3 + '@alcalzone/ansi-tokenize@0.3.0': + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + '@alloc/quick-lru@5.2.0': {} '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.156': @@ -16157,10 +15970,6 @@ snapshots: '@aws/lambda-invoke-store@0.2.4': {} - '@babel/code-frame@7.10.4': - dependencies: - '@babel/highlight': 7.25.9 - '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -16284,6 +16093,8 @@ snapshots: '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.29.7': {} + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16332,13 +16143,6 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.7 - '@babel/highlight@7.25.9': - dependencies: - '@babel/helper-validator-identifier': 7.29.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.1 - '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.7 @@ -16350,7 +16154,7 @@ snapshots: '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: @@ -16494,18 +16298,10 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -16513,20 +16309,8 @@ snapshots: '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-classes@7.28.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-globals': 7.28.0 + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color @@ -16542,12 +16326,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/template': 7.28.6 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16575,20 +16353,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16608,21 +16372,11 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16639,14 +16393,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16663,7 +16409,7 @@ snapshots: '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -16672,7 +16418,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color @@ -16716,10 +16462,10 @@ snapshots: '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)': dependencies: @@ -16738,19 +16484,6 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16773,29 +16506,6 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-react@7.28.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - - '@babel/preset-typescript@7.27.1(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) - transitivePeerDependencies: - - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -16839,27 +16549,27 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.6(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@base-ui/utils': 0.2.6(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@floating-ui/utils': 0.2.11 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 - '@base-ui/utils@0.2.6(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@base-ui/utils@0.2.6(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.2 '@floating-ui/utils': 0.2.11 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 @@ -17152,13 +16862,13 @@ snapshots: '@dnd-kit/state': 0.1.21 tslib: 2.8.1 - '@dnd-kit/react@0.1.21(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@dnd-kit/react@0.1.21(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@dnd-kit/abstract': 0.1.21 '@dnd-kit/dom': 0.1.21 '@dnd-kit/state': 0.1.21 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) tslib: 2.8.1 '@dnd-kit/state@0.1.21': @@ -17181,9 +16891,9 @@ snapshots: '@drizzle-team/brocli@0.10.2': {} - '@earendil-works/pi-agent-core@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3)': + '@earendil-works/pi-agent-core@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3)': dependencies: - '@earendil-works/pi-ai': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + '@earendil-works/pi-ai': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) ignore: 7.0.5 typebox: 1.1.38 yaml: 2.9.0 @@ -17195,7 +16905,7 @@ snapshots: - ws - zod - '@earendil-works/pi-ai@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3)': + '@earendil-works/pi-ai@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3)': dependencies: '@anthropic-ai/sdk': 0.91.1(zod@4.4.3) '@aws-sdk/client-bedrock-runtime': 3.1048.0 @@ -17205,7 +16915,7 @@ snapshots: '@smithy/node-http-handler': 4.7.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - openai: 6.26.0(ws@8.19.0)(zod@4.4.3) + openai: 6.26.0(ws@8.21.1)(zod@4.4.3) partial-json: 0.1.7 typebox: 1.1.38 transitivePeerDependencies: @@ -17216,10 +16926,10 @@ snapshots: - ws - zod - '@earendil-works/pi-coding-agent@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3)': + '@earendil-works/pi-coding-agent@0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3)': dependencies: - '@earendil-works/pi-agent-core': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) - '@earendil-works/pi-ai': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.19.0)(zod@4.4.3) + '@earendil-works/pi-agent-core': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) + '@earendil-works/pi-ai': 0.80.6(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.1)(zod@4.4.3) '@earendil-works/pi-tui': 0.80.6 '@silvia-odwyer/photon-node': 0.3.4 chalk: 5.6.2 @@ -17355,12 +17065,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/core@1.11.1': - dependencies: - '@emnapi/wasi-threads': 1.2.2 - tslib: 2.8.1 - optional: true - '@emnapi/core@1.11.2': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -17384,11 +17088,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.11.1': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 @@ -17759,32 +17458,36 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 - '@expo/cli@54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))': + '@expo-google-fonts/material-symbols@0.4.42': {} + + '@expo/cli@57.0.10(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-constants@57.0.7)(expo-font@57.0.1)(expo-router@57.0.8)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3)': dependencies: - '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 + '@expo/config': 57.0.6(typescript@5.9.3) + '@expo/config-plugins': 57.0.6(typescript@5.9.3) '@expo/devcert': 1.2.1 - '@expo/env': 2.0.8 - '@expo/image-utils': 0.8.8 - '@expo/json-file': 10.0.8 - '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.14(expo@54.0.33) - '@expo/osascript': 2.3.8 - '@expo/package-manager': 1.9.10 - '@expo/plist': 0.4.8 - '@expo/prebuild-config': 54.0.8(expo@54.0.33) - '@expo/schema-utils': 0.1.8 - '@expo/spawn-async': 1.7.2 - '@expo/ws-tunnel': 1.0.6 - '@expo/xcpretty': 4.4.0 - '@react-native/dev-middleware': 0.81.5 - '@urql/core': 5.2.0(graphql@16.12.0) - '@urql/exchange-retry': 1.3.2(@urql/core@5.2.0(graphql@16.12.0)) + '@expo/env': 2.4.2 + '@expo/image-utils': 0.11.4(typescript@5.9.3) + '@expo/inline-modules': 0.1.3(typescript@5.9.3) + '@expo/json-file': 11.0.1 + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro': 56.0.0 + '@expo/metro-config': 57.0.7(expo@57.0.8)(typescript@5.9.3) + '@expo/metro-file-map': 57.0.1 + '@expo/osascript': 2.7.1 + '@expo/package-manager': 1.13.1 + '@expo/plist': 0.8.1 + '@expo/prebuild-config': 57.0.9(typescript@5.9.3) + '@expo/require-utils': 57.0.4(typescript@5.9.3) + '@expo/router-server': 57.0.4(@expo/metro-runtime@57.0.7)(expo-constants@57.0.7)(expo-font@57.0.1)(expo-router@57.0.8)(expo-server@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@expo/schema-utils': 57.0.2 + '@expo/spawn-async': 1.8.0 + '@expo/ws-tunnel': 2.0.0(ws@8.19.0) + '@expo/xcpretty': 4.4.4 + '@react-native/dev-middleware': 0.86.0 accepts: 1.3.8 + agent-cli-detector: 0.1.4 arg: 5.0.2 - better-opn: 3.0.2 bplist-creator: 0.1.0 bplist-parser: 0.3.2 chalk: 4.1.2 @@ -17792,90 +17495,88 @@ snapshots: compression: 1.8.1 connect: 3.7.0 debug: 4.4.3 - env-editor: 0.4.2 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-server: 1.0.5 - freeport-async: 2.0.0 + dnssd-advertise: 1.1.6 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-server: 57.0.1 + fetch-nodeshim: 0.4.10 getenv: 2.0.0 glob: 13.0.6 - lan-network: 0.1.7 - minimatch: 9.0.5 + lan-network: 0.2.1 + multitars: 1.0.0 node-forge: 1.3.3 npm-package-arg: 11.0.3 ora: 3.4.0 - picomatch: 3.0.1 - pretty-bytes: 5.6.0 + picomatch: 4.0.5 pretty-format: 29.7.0 progress: 2.0.3 prompts: 2.4.2 - qrcode-terminal: 0.11.0 - require-from-string: 2.0.2 - requireg: 0.2.2 - resolve: 1.22.11 resolve-from: 5.0.0 - resolve.exports: 2.0.3 semver: 7.8.4 send: 0.19.2 slugify: 1.6.6 - source-map-support: 0.5.21 stacktrace-parser: 0.1.11 structured-headers: 0.4.1 - tar: 7.5.7 terminal-link: 2.1.1 - undici: 6.23.0 + toqr: 0.1.1 wrap-ansi: 7.0.0 ws: 8.19.0 + zod: 3.25.76 optionalDependencies: - expo-router: 6.0.23(76047f2336d892e43bef2ac48cb56303) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo-router: 57.0.8(507433f30d6a233882de5eb4189dda28) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: + - '@expo/dom-webview' + - '@expo/metro-runtime' - bufferutil - - graphql + - expo-constants + - expo-font + - react + - react-dom + - react-server-dom-webpack - supports-color + - typescript - utf-8-validate '@expo/code-signing-certificates@0.0.6': dependencies: node-forge: 1.3.3 - '@expo/config-plugins@54.0.4': + '@expo/config-plugins@57.0.6(typescript@5.9.3)': dependencies: - '@expo/config-types': 54.0.10 - '@expo/json-file': 10.0.8 - '@expo/plist': 0.4.8 + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/plist': 0.8.1 + '@expo/require-utils': 57.0.4(typescript@5.9.3) '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 debug: 4.4.3 getenv: 2.0.0 glob: 13.0.6 - resolve-from: 5.0.0 semver: 7.8.4 - slash: 3.0.0 slugify: 1.6.6 xcode: 3.0.1 xml2js: 0.6.0 transitivePeerDependencies: - supports-color + - typescript - '@expo/config-types@54.0.10': {} + '@expo/config-types@57.0.2': {} - '@expo/config@12.0.13': + '@expo/config@57.0.6(typescript@5.9.3)': dependencies: - '@babel/code-frame': 7.10.4 - '@expo/config-plugins': 54.0.4 - '@expo/config-types': 54.0.10 - '@expo/json-file': 10.0.8 + '@expo/config-plugins': 57.0.6(typescript@5.9.3) + '@expo/config-types': 57.0.2 + '@expo/json-file': 11.0.1 + '@expo/require-utils': 57.0.4(typescript@5.9.3) deepmerge: 4.3.1 getenv: 2.0.0 glob: 13.0.6 - require-from-string: 2.0.2 - resolve-from: 5.0.0 resolve-workspace-root: 2.0.1 semver: 7.8.4 slugify: 1.6.6 - sucrase: 3.35.1 transitivePeerDependencies: - supports-color + - typescript '@expo/devcert@1.2.1': dependencies: @@ -17884,182 +17585,255 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devtools@0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@expo/devtools@57.0.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': dependencies: chalk: 4.1.2 optionalDependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + + '@expo/dom-webview@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + dependencies: + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - '@expo/env@2.0.8': + '@expo/env@2.4.2': dependencies: chalk: 4.1.2 debug: 4.4.3 - dotenv: 16.4.7 - dotenv-expand: 11.0.7 getenv: 2.0.0 transitivePeerDependencies: - supports-color - '@expo/fingerprint@0.15.4': + '@expo/expo-modules-macros-plugin@0.6.1': {} + + '@expo/fingerprint@0.20.6': dependencies: - '@expo/spawn-async': 1.7.2 + '@expo/env': 2.4.2 + '@expo/spawn-async': 1.8.0 arg: 5.0.2 chalk: 4.1.2 debug: 4.4.3 getenv: 2.0.0 glob: 13.0.6 ignore: 5.3.2 - minimatch: 9.0.5 - p-limit: 3.1.0 + minimatch: 10.2.5 resolve-from: 5.0.0 semver: 7.8.4 transitivePeerDependencies: - supports-color - '@expo/image-utils@0.8.8': + '@expo/image-utils@0.11.4(typescript@5.9.3)': dependencies: - '@expo/spawn-async': 1.7.2 + '@expo/require-utils': 57.0.4(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 getenv: 2.0.0 jimp-compact: 0.16.1 parse-png: 2.1.0 - resolve-from: 5.0.0 - resolve-global: 1.0.0 semver: 7.8.4 - temp-dir: 2.0.0 - unique-string: 2.0.0 + transitivePeerDependencies: + - supports-color + - typescript - '@expo/json-file@10.0.8': + '@expo/inline-modules@0.1.3(typescript@5.9.3)': dependencies: - '@babel/code-frame': 7.10.4 - json5: 2.2.3 - - '@expo/metro-config@54.0.14(expo@54.0.33)': + '@expo/config-plugins': 57.0.6(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/json-file@11.0.1': + dependencies: + '@babel/code-frame': 7.29.0 + json5: 2.2.3 + + '@expo/local-build-cache-provider@57.0.4(typescript@5.9.3)': + dependencies: + '@expo/config': 57.0.6(typescript@5.9.3) + chalk: 4.1.2 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/log-box@57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + dependencies: + '@expo/dom-webview': 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + anser: 1.4.10 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + stacktrace-parser: 0.1.11 + + '@expo/metro-config@57.0.7(expo@57.0.8)(typescript@5.9.3)': dependencies: '@babel/code-frame': 7.29.0 '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@expo/config': 12.0.13 - '@expo/env': 2.0.8 - '@expo/json-file': 10.0.8 - '@expo/metro': 54.2.0 - '@expo/spawn-async': 1.7.2 + '@expo/config': 57.0.6(typescript@5.9.3) + '@expo/env': 2.4.2 + '@expo/json-file': 11.0.1 + '@expo/metro': 56.0.0 + '@expo/require-utils': 57.0.4(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 browserslist: 4.28.1 chalk: 4.1.2 debug: 4.4.3 - dotenv: 16.4.7 - dotenv-expand: 11.0.7 getenv: 2.0.0 glob: 13.0.6 - hermes-parser: 0.29.1 + hermes-parser: 0.36.1 jsc-safe-url: 0.2.4 - lightningcss: 1.31.1 - minimatch: 9.0.5 - postcss: 8.4.49 + lightningcss: 1.32.0 + picomatch: 4.0.5 + postcss: 8.5.15 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) transitivePeerDependencies: - bufferutil - supports-color + - typescript - utf-8-validate - '@expo/metro-runtime@6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@expo/metro-file-map@57.0.1': dependencies: + debug: 4.4.3 + fb-watchman: 2.0.2 + invariant: 2.2.4 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + transitivePeerDependencies: + - supports-color + + '@expo/metro-runtime@57.0.7(@expo/log-box@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + dependencies: + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) anser: 1.4.10 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) pretty-format: 29.7.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 optionalDependencies: - react-dom: 19.1.0(react@19.1.0) - - '@expo/metro@54.2.0': - dependencies: - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-config: 0.83.3 - metro-core: 0.83.3 - metro-file-map: 0.83.3 - metro-minify-terser: 0.83.3 - metro-resolver: 0.83.3 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 + react-dom: 19.2.6(react@19.2.6) + + '@expo/metro@56.0.0': + dependencies: + metro: 0.84.4 + metro-babel-transformer: 0.84.4 + metro-cache: 0.84.4 + metro-cache-key: 0.84.4 + metro-config: 0.84.4 + metro-core: 0.84.4 + metro-file-map: 0.84.4 + metro-minify-terser: 0.84.4 + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4 + metro-symbolicate: 0.84.4 + metro-transform-plugins: 0.84.4 + metro-transform-worker: 0.84.4 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/osascript@2.3.8': + '@expo/osascript@2.7.1': dependencies: - '@expo/spawn-async': 1.7.2 - exec-async: 2.2.0 + '@expo/spawn-async': 1.8.0 - '@expo/package-manager@1.9.10': + '@expo/package-manager@1.13.1': dependencies: - '@expo/json-file': 10.0.8 - '@expo/spawn-async': 1.7.2 + '@expo/json-file': 11.0.1 + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 npm-package-arg: 11.0.3 ora: 3.4.0 resolve-workspace-root: 2.0.1 - '@expo/plist@0.4.8': + '@expo/plist@0.8.1': dependencies: '@xmldom/xmldom': 0.8.11 base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@54.0.8(expo@54.0.33)': + '@expo/prebuild-config@57.0.9(typescript@5.9.3)': dependencies: - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 - '@expo/config-types': 54.0.10 - '@expo/image-utils': 0.8.8 - '@expo/json-file': 10.0.8 - '@react-native/normalize-colors': 0.81.5 + '@expo/config': 57.0.6(typescript@5.9.3) + '@expo/config-plugins': 57.0.6(typescript@5.9.3) + '@expo/config-types': 57.0.2 + '@expo/image-utils': 0.11.4(typescript@5.9.3) + '@expo/json-file': 11.0.1 + '@react-native/normalize-colors': 0.86.0 debug: 4.4.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo-modules-autolinking: 57.0.9(typescript@5.9.3) resolve-from: 5.0.0 semver: 7.8.4 - xml2js: 0.6.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@expo/require-utils@57.0.4(typescript@5.9.3)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@expo/schema-utils@0.1.8': {} + '@expo/router-server@57.0.4(@expo/metro-runtime@57.0.7)(expo-constants@57.0.7)(expo-font@57.0.1)(expo-router@57.0.8)(expo-server@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + debug: 4.4.3 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-font: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-server: 57.0.1 + react: 19.2.6 + optionalDependencies: + '@expo/metro-runtime': 57.0.7(@expo/log-box@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-router: 57.0.8(507433f30d6a233882de5eb4189dda28) + react-dom: 19.2.6(react@19.2.6) + transitivePeerDependencies: + - supports-color + + '@expo/schema-utils@57.0.2': {} '@expo/sdk-runtime-versions@1.0.0': {} - '@expo/spawn-async@1.7.2': + '@expo/spawn-async@1.8.0': dependencies: cross-spawn: 7.0.6 '@expo/sudo-prompt@9.3.2': {} - '@expo/ui@0.2.0-beta.9(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@expo/ui@57.0.7(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) sf-symbols-typescript: 2.2.0 + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + optionalDependencies: + '@babel/core': 7.29.0 + react-dom: 19.2.6(react@19.2.6) + react-native-worklets: 0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' - '@expo/vector-icons@15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@expo/ws-tunnel@2.0.0(ws@8.19.0)': dependencies: - expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - - '@expo/ws-tunnel@1.0.6': {} + ws: 8.19.0 - '@expo/xcpretty@4.4.0': + '@expo/xcpretty@4.4.4': dependencies: '@babel/code-frame': 7.29.0 chalk: 4.1.2 @@ -18085,18 +17859,18 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - '@floating-ui/react@0.27.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@floating-ui/react@0.27.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@floating-ui/utils': 0.2.11 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) tabbable: 6.4.0 '@floating-ui/utils@0.2.11': {} @@ -18149,8 +17923,6 @@ snapshots: '@iarna/toml@2.2.5': {} - '@ide/backoff@1.0.0': {} - '@inquirer/ansi@1.0.2': {} '@inquirer/confirm@5.1.21(@types/node@20.19.41)': @@ -18365,23 +18137,12 @@ snapshots: - supports-color - ts-node - '@jest/create-cache-key-function@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@jest/create-cache-key-function@30.4.1': dependencies: '@jest/types': 30.4.1 '@jest/diff-sequences@30.4.0': {} - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.12.0 - jest-mock: 29.7.0 - '@jest/environment@30.4.1': dependencies: '@jest/fake-timers': 30.4.1 @@ -18400,15 +18161,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 24.12.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - '@jest/fake-timers@30.4.1': dependencies: '@jest/types': 30.4.1 @@ -18497,26 +18249,6 @@ snapshots: jest-haste-map: 30.4.1 slash: 3.0.0 - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.29.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - '@jest/transform@30.4.1': dependencies: '@babel/core': 7.29.0 @@ -18816,10 +18548,10 @@ snapshots: dependencies: zod: 4.4.3 - '@json-render/react@0.19.0(react@19.1.0)(zod@4.4.3)': + '@json-render/react@0.19.0(react@19.2.6)(zod@4.4.3)': dependencies: '@json-render/core': 0.19.0(zod@4.4.3) - react: 19.1.0 + react: 19.2.6 transitivePeerDependencies: - zod @@ -19110,11 +18842,11 @@ snapshots: '@mariozechner/clipboard-win32-x64-msvc': 0.3.9 optional: true - '@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.1.0)': + '@mdx-js/react@3.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: '@types/mdx': 2.0.13 '@types/react': 19.2.17 - react: 19.1.0 + react: 19.2.6 '@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)': dependencies: @@ -19130,14 +18862,6 @@ snapshots: '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/ext-apps@1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(zod@4.4.3)': - dependencies: - '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) - zod: 4.4.3 - optionalDependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - '@modelcontextprotocol/ext-apps@1.2.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)': dependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) @@ -19227,13 +18951,6 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.3 - optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: '@emnapi/core': 1.11.2 @@ -19427,7 +19144,7 @@ snapshots: '@oxc-parser/binding-android-arm-eabi@0.127.0': optional: true - '@oxc-parser/binding-android-arm-eabi@0.138.0': + '@oxc-parser/binding-android-arm-eabi@0.141.0': optional: true '@oxc-parser/binding-android-arm64@0.120.0': @@ -19436,7 +19153,7 @@ snapshots: '@oxc-parser/binding-android-arm64@0.127.0': optional: true - '@oxc-parser/binding-android-arm64@0.138.0': + '@oxc-parser/binding-android-arm64@0.141.0': optional: true '@oxc-parser/binding-darwin-arm64@0.120.0': @@ -19445,7 +19162,7 @@ snapshots: '@oxc-parser/binding-darwin-arm64@0.127.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.138.0': + '@oxc-parser/binding-darwin-arm64@0.141.0': optional: true '@oxc-parser/binding-darwin-x64@0.120.0': @@ -19454,7 +19171,7 @@ snapshots: '@oxc-parser/binding-darwin-x64@0.127.0': optional: true - '@oxc-parser/binding-darwin-x64@0.138.0': + '@oxc-parser/binding-darwin-x64@0.141.0': optional: true '@oxc-parser/binding-freebsd-x64@0.120.0': @@ -19463,7 +19180,7 @@ snapshots: '@oxc-parser/binding-freebsd-x64@0.127.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.138.0': + '@oxc-parser/binding-freebsd-x64@0.141.0': optional: true '@oxc-parser/binding-linux-arm-gnueabihf@0.120.0': @@ -19472,7 +19189,7 @@ snapshots: '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.138.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': optional: true '@oxc-parser/binding-linux-arm-musleabihf@0.120.0': @@ -19481,7 +19198,7 @@ snapshots: '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.138.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': optional: true '@oxc-parser/binding-linux-arm64-gnu@0.120.0': @@ -19490,7 +19207,7 @@ snapshots: '@oxc-parser/binding-linux-arm64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.138.0': + '@oxc-parser/binding-linux-arm64-gnu@0.141.0': optional: true '@oxc-parser/binding-linux-arm64-musl@0.120.0': @@ -19499,7 +19216,7 @@ snapshots: '@oxc-parser/binding-linux-arm64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.138.0': + '@oxc-parser/binding-linux-arm64-musl@0.141.0': optional: true '@oxc-parser/binding-linux-ppc64-gnu@0.120.0': @@ -19508,7 +19225,7 @@ snapshots: '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.138.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': optional: true '@oxc-parser/binding-linux-riscv64-gnu@0.120.0': @@ -19517,7 +19234,7 @@ snapshots: '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.138.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': optional: true '@oxc-parser/binding-linux-riscv64-musl@0.120.0': @@ -19526,7 +19243,7 @@ snapshots: '@oxc-parser/binding-linux-riscv64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.138.0': + '@oxc-parser/binding-linux-riscv64-musl@0.141.0': optional: true '@oxc-parser/binding-linux-s390x-gnu@0.120.0': @@ -19535,7 +19252,7 @@ snapshots: '@oxc-parser/binding-linux-s390x-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.138.0': + '@oxc-parser/binding-linux-s390x-gnu@0.141.0': optional: true '@oxc-parser/binding-linux-x64-gnu@0.120.0': @@ -19544,7 +19261,7 @@ snapshots: '@oxc-parser/binding-linux-x64-gnu@0.127.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.138.0': + '@oxc-parser/binding-linux-x64-gnu@0.141.0': optional: true '@oxc-parser/binding-linux-x64-musl@0.120.0': @@ -19553,7 +19270,7 @@ snapshots: '@oxc-parser/binding-linux-x64-musl@0.127.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.138.0': + '@oxc-parser/binding-linux-x64-musl@0.141.0': optional: true '@oxc-parser/binding-openharmony-arm64@0.120.0': @@ -19562,7 +19279,7 @@ snapshots: '@oxc-parser/binding-openharmony-arm64@0.127.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.138.0': + '@oxc-parser/binding-openharmony-arm64@0.141.0': optional: true '@oxc-parser/binding-wasm32-wasi@0.120.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': @@ -19580,11 +19297,11 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) optional: true - '@oxc-parser/binding-wasm32-wasi@0.138.0': + '@oxc-parser/binding-wasm32-wasi@0.141.0': dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@oxc-parser/binding-win32-arm64-msvc@0.120.0': @@ -19593,7 +19310,7 @@ snapshots: '@oxc-parser/binding-win32-arm64-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.138.0': + '@oxc-parser/binding-win32-arm64-msvc@0.141.0': optional: true '@oxc-parser/binding-win32-ia32-msvc@0.120.0': @@ -19602,7 +19319,7 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.138.0': + '@oxc-parser/binding-win32-ia32-msvc@0.141.0': optional: true '@oxc-parser/binding-win32-x64-msvc@0.120.0': @@ -19611,7 +19328,7 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.127.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.138.0': + '@oxc-parser/binding-win32-x64-msvc@0.141.0': optional: true '@oxc-project/runtime@0.101.0': {} @@ -19622,7 +19339,7 @@ snapshots: '@oxc-project/types@0.127.0': {} - '@oxc-project/types@0.138.0': {} + '@oxc-project/types@0.141.0': {} '@oxc-resolver/binding-android-arm-eabi@11.17.0': optional: true @@ -19865,61 +19582,61 @@ snapshots: '@oxfmt/binding-win32-x64-msvc@0.45.0': optional: true - '@oxlint/binding-android-arm-eabi@1.66.0': + '@oxlint/binding-android-arm-eabi@1.74.0': optional: true - '@oxlint/binding-android-arm64@1.66.0': + '@oxlint/binding-android-arm64@1.74.0': optional: true - '@oxlint/binding-darwin-arm64@1.66.0': + '@oxlint/binding-darwin-arm64@1.74.0': optional: true - '@oxlint/binding-darwin-x64@1.66.0': + '@oxlint/binding-darwin-x64@1.74.0': optional: true - '@oxlint/binding-freebsd-x64@1.66.0': + '@oxlint/binding-freebsd-x64@1.74.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.66.0': + '@oxlint/binding-linux-arm-gnueabihf@1.74.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.66.0': + '@oxlint/binding-linux-arm-musleabihf@1.74.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.66.0': + '@oxlint/binding-linux-arm64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.66.0': + '@oxlint/binding-linux-arm64-musl@1.74.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.66.0': + '@oxlint/binding-linux-ppc64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.66.0': + '@oxlint/binding-linux-riscv64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.66.0': + '@oxlint/binding-linux-riscv64-musl@1.74.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.66.0': + '@oxlint/binding-linux-s390x-gnu@1.74.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.66.0': + '@oxlint/binding-linux-x64-gnu@1.74.0': optional: true - '@oxlint/binding-linux-x64-musl@1.66.0': + '@oxlint/binding-linux-x64-musl@1.74.0': optional: true - '@oxlint/binding-openharmony-arm64@1.66.0': + '@oxlint/binding-openharmony-arm64@1.74.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.66.0': + '@oxlint/binding-win32-arm64-msvc@1.74.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.66.0': + '@oxlint/binding-win32-ia32-msvc@1.74.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.66.0': + '@oxlint/binding-win32-x64-msvc@1.74.0': optional: true '@parcel/watcher-android-arm64@2.5.6': @@ -20004,24 +19721,10 @@ snapshots: tslib: 2.8.1 webcrypto-core: 1.9.2 - '@phosphor-icons/react@2.1.10(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@phosphor-icons/react@2.1.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - - '@pierre/diffs@1.2.10(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': - dependencies: - '@pierre/theme': 1.0.3 - '@pierre/theming': 0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(shiki@3.23.0) - '@shikijs/transformers': 3.23.0 - diff: 8.0.3 - hast-util-to-html: 9.0.5 - lru_map: 0.4.1 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - shiki: 3.23.0 - transitivePeerDependencies: - - '@shikijs/themes' + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) '@pierre/diffs@1.2.10(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -20039,14 +19742,6 @@ snapshots: '@pierre/theme@1.0.3': {} - '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(shiki@3.23.0)': - optionalDependencies: - '@pierre/theme': 1.0.3 - '@shikijs/themes': 3.23.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - shiki: 3.23.0 - '@pierre/theming@0.0.1(@pierre/theme@1.0.3)(@shikijs/themes@3.23.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(shiki@3.23.0)': optionalDependencies: '@pierre/theme': 1.0.3 @@ -20096,15 +19791,15 @@ snapshots: dependencies: '@posthog/types': 1.386.4 - '@posthog/hedgehog-mode@0.0.53(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@posthog/hedgehog-mode@0.0.53(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: gsap: 3.14.2 lodash: 4.17.23 matter-js: 0.20.0 pixi.js: 8.16.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-shadow: 20.6.0(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-shadow: 20.6.0(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) uuid: 12.0.0 transitivePeerDependencies: - prop-types @@ -20113,39 +19808,39 @@ snapshots: dependencies: cross-spawn: 7.0.6 - '@posthog/quill-charts@0.3.0-beta.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@posthog/quill-charts@0.3.0-beta.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@floating-ui/react': 0.27.19(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@floating-ui/react': 0.27.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6) d3-array: 3.2.4 d3-color: 3.1.0 d3-scale: 4.0.2 d3-shape: 3.2.0 dayjs: 1.11.11 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) simple-statistics: 7.8.9 - '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.2.2)': + '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.2.2)': dependencies: - '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) class-variance-authority: 0.7.1 clsx: 2.1.1 - lucide-react: 0.577.0(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-resizable-panels: 4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + lucide-react: 0.577.0(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-resizable-panels: 4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tailwind-merge: 2.6.1 tailwindcss: 4.2.2 - '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tailwindcss@4.3.1)': + '@posthog/quill@0.3.0-beta.24(@base-ui/react@1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tailwindcss@4.3.1)': dependencies: - '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@base-ui/react': 1.3.0(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) class-variance-authority: 0.7.1 clsx: 2.1.1 - lucide-react: 0.577.0(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-resizable-panels: 4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + lucide-react: 0.577.0(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-resizable-panels: 4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tailwind-merge: 2.6.1 tailwindcss: 4.3.1 @@ -20197,789 +19892,782 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-context@1.1.2(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-context@1.1.2(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) aria-hidden: 1.2.6 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-direction@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-icons@1.3.2(react@19.1.0)': + '@radix-ui/react-icons@1.3.2(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 - '@radix-ui/react-id@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-id@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) aria-hidden: 1.2.6 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) aria-hidden: 1.2.6 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) '@radix-ui/rect': 1.1.1 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) aria-hidden: 1.2.6 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.2.0(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - optionalDependencies: - '@types/react': 19.2.17 - - '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.1.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 - use-sync-external-store: 1.6.0(react@19.1.0) + react: 19.2.6 + use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: '@radix-ui/rect': 1.1.1 - react: 19.1.0 + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.17)(react@19.1.0)': + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.17)(react@19.2.6)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - react: 19.1.0 + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + react: 19.2.6 optionalDependencies: '@types/react': 19.2.17 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) '@radix-ui/rect@1.1.1': {} - '@radix-ui/themes@3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/themes@3.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@radix-ui/colors': 3.0.0 classnames: 2.5.1 - radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.1.0) + radix-ui: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@react-grab/cli@0.1.48': + '@react-grab/cli@0.1.50': dependencies: agent-install: 0.0.6 commander: 14.0.3 @@ -20990,27 +20678,32 @@ snapshots: prompts: 2.4.2 tinyexec: 1.2.4 - '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))': + '@react-native-async-storage/async-storage@2.2.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))': dependencies: merge-options: 3.0.4 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - '@react-native-community/netinfo@12.0.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@react-native-community/netinfo@12.0.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - '@react-native/assets-registry@0.81.5': {} + '@react-native-masked-view/masked-view@0.3.2(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': + dependencies: + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + + '@react-native/assets-registry@0.86.0': {} - '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.29.0)': + '@react-native/babel-plugin-codegen@0.86.0(@babel/core@7.29.0)': dependencies: '@babel/traverse': 7.29.0 - '@react-native/codegen': 0.81.5(@babel/core@7.29.0) + '@react-native/codegen': 0.86.0(@babel/core@7.29.0) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.81.5(@babel/core@7.29.0)': + '@react-native/babel-preset@0.86.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) @@ -21018,171 +20711,155 @@ snapshots: '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.0) '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/template': 7.28.6 - '@react-native/babel-plugin-codegen': 0.81.5(@babel/core@7.29.0) - babel-plugin-syntax-hermes-parser: 0.29.1 + '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.0) + babel-plugin-syntax-hermes-parser: 0.36.0 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.81.5(@babel/core@7.29.0)': + '@react-native/codegen@0.86.0(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - glob: 7.2.3 - hermes-parser: 0.29.1 + '@babel/parser': 7.29.7 + hermes-parser: 0.36.0 invariant: 2.2.4 nullthrows: 1.1.1 + tinyglobby: 0.2.15 yargs: 17.7.2 - '@react-native/community-cli-plugin@0.81.5': + '@react-native/community-cli-plugin@0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.0))': dependencies: - '@react-native/dev-middleware': 0.81.5 + '@react-native/dev-middleware': 0.86.0 debug: 4.4.3 invariant: 2.2.4 - metro: 0.83.3 - metro-config: 0.83.3 - metro-core: 0.83.3 + metro: 0.84.4 + metro-config: 0.84.4 + metro-core: 0.84.4 semver: 7.8.4 + optionalDependencies: + '@react-native/metro-config': 0.86.0(@babel/core@7.29.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.81.5': {} + '@react-native/debugger-frontend@0.86.0': {} - '@react-native/dev-middleware@0.81.5': + '@react-native/debugger-shell@0.86.0': + dependencies: + cross-spawn: 7.0.6 + debug: 4.4.3 + fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color + + '@react-native/dev-middleware@0.86.0': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.81.5 + '@react-native/debugger-frontend': 0.86.0 + '@react-native/debugger-shell': 0.86.0 chrome-launcher: 0.15.2 - chromium-edge-launcher: 0.2.0 + chromium-edge-launcher: 0.3.0 connect: 3.7.0 debug: 4.4.3 invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 serve-static: 1.16.3 - ws: 6.2.3 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/gradle-plugin@0.81.5': {} + '@react-native/gradle-plugin@0.86.0': {} + + '@react-native/js-polyfills@0.86.0': {} + + '@react-native/metro-babel-transformer@0.86.0(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@react-native/babel-preset': 0.86.0(@babel/core@7.29.0) + hermes-parser: 0.36.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - supports-color - '@react-native/js-polyfills@0.81.5': {} + '@react-native/metro-config@0.86.0(@babel/core@7.29.0)': + dependencies: + '@react-native/js-polyfills': 0.86.0 + '@react-native/metro-babel-transformer': 0.86.0(@babel/core@7.29.0) + metro-config: 0.84.4 + metro-runtime: 0.84.4 + transitivePeerDependencies: + - '@babel/core' + - supports-color '@react-native/normalize-colors@0.74.89': {} - '@react-native/normalize-colors@0.81.5': {} + '@react-native/normalize-colors@0.86.0': {} - '@react-native/virtualized-lists@0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@react-native/virtualized-lists@0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 - '@react-navigation/bottom-tabs@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': - dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - color: 4.2.3 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - sf-symbols-typescript: 2.2.0 - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@react-navigation/core@7.14.0(react@19.1.0)': + '@react-navigation/core@7.14.0(react@19.2.6)': dependencies: '@react-navigation/routers': 7.5.3 escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.11 + nanoid: 3.3.12 query-string: 7.1.3 - react: 19.1.0 + react: 19.2.6 react-is: 19.2.6 - use-latest-callback: 0.2.6(react@19.1.0) - use-sync-external-store: 1.6.0(react@19.1.0) - - '@react-navigation/elements@2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': - dependencies: - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - color: 4.2.3 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - use-latest-callback: 0.2.6(react@19.1.0) - use-sync-external-store: 1.6.0(react@19.1.0) - - '@react-navigation/native-stack@7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': - dependencies: - '@react-navigation/elements': 2.9.5(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - color: 4.2.3 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - sf-symbols-typescript: 2.2.0 - warn-once: 0.1.1 - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' + use-latest-callback: 0.2.6(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.2.6) + optional: true - '@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/native@7.1.28(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)': dependencies: - '@react-navigation/core': 7.14.0(react@19.1.0) + '@react-navigation/core': 7.14.0(react@19.2.6) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.11 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - use-latest-callback: 0.2.6(react@19.1.0) + nanoid: 3.3.12 + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + use-latest-callback: 0.2.6(react@19.2.6) + optional: true '@react-navigation/routers@7.5.3': dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 + optional: true '@remirror/core-constants@3.0.0': {} @@ -21373,6 +21050,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@shaderfrog/glsl-parser@7.0.1': {} + '@shikijs/core@3.23.0': dependencies: '@shikijs/types': 3.23.0 @@ -21441,10 +21120,6 @@ snapshots: dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers@15.4.0': dependencies: '@sinonjs/commons': 3.0.1 @@ -21507,21 +21182,21 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addon-a11y@10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/addon-a11y@10.4.1(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.11.1 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/addon-docs@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.1.0) - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - '@storybook/icons': 2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@mdx-js/react': 3.1.1(@types/react@19.2.17)(react@19.2.6) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ts-dedent: 2.2.0 optionalDependencies: '@types/react': 19.2.17 @@ -21532,10 +21207,10 @@ snapshots: - vite - webpack - '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/builder-vite@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@storybook/csf-plugin': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) ts-dedent: 2.2.0 vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: @@ -21543,9 +21218,9 @@ snapshots: - rollup - webpack - '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/csf-plugin@10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) unplugin: 2.3.11 optionalDependencies: esbuild: 0.27.2 @@ -21555,33 +21230,33 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/icons@2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@storybook/icons@2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/react-dom-shim@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@storybook/react-vite@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.27.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@5.9.3) '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) - '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3) + '@storybook/builder-vite': 10.4.1(esbuild@0.27.2)(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(rollup@4.57.1)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)) + '@storybook/react': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3) empathic: 2.0.0 magic-string: 0.30.21 - react: 19.1.0 + react: 19.2.6 react-docgen: 8.0.2 - react-dom: 19.1.0(react@19.1.0) + react-dom: 19.2.6(react@19.2.6) resolve: 1.22.11 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) tsconfig-paths: 4.2.0 vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: @@ -21593,15 +21268,15 @@ snapshots: - typescript - webpack - '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(typescript@5.9.3)': + '@storybook/react@10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) - react: 19.1.0 + '@storybook/react-dom-shim': 10.4.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)) + react: 19.2.6 react-docgen: 8.0.2 react-docgen-typescript: 2.4.0(typescript@5.9.3) - react-dom: 19.1.0(react@19.1.0) - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-dom: 19.2.6(react@19.2.6) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -21609,7 +21284,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/test-runner@0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@storybook/test-runner@0.24.4(@types/node@24.12.0)(esbuild-register@3.6.0(esbuild@0.27.2))(storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 @@ -21631,7 +21306,7 @@ snapshots: playwright: 1.60.0 playwright-core: 1.60.0 rimraf: 3.0.2 - storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + storybook: 10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) uuid: 8.3.2 transitivePeerDependencies: - '@swc/helpers' @@ -21964,48 +21639,48 @@ snapshots: '@tanstack/query-core@5.90.20': {} - '@tanstack/react-query@5.101.0(react@19.1.0)': + '@tanstack/react-query@5.101.0(react@19.2.6)': dependencies: '@tanstack/query-core': 5.101.0 - react: 19.1.0 + react: 19.2.6 - '@tanstack/react-query@5.90.20(react@19.1.0)': + '@tanstack/react-query@5.90.20(react@19.2.6)': dependencies: '@tanstack/query-core': 5.90.20 - react: 19.1.0 + react: 19.2.6 - '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-router-devtools@1.167.0(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@tanstack/router-core@1.171.13)(csstype@3.2.3)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/router-devtools-core': 1.168.0(@tanstack/router-core@1.171.13)(csstype@3.2.3) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@tanstack/router-core': 1.171.13 transitivePeerDependencies: - csstype - '@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@tanstack/history': 1.162.0 - '@tanstack/react-store': 0.9.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-store': 0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@tanstack/router-core': 1.171.13 isbot: 5.1.40 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - '@tanstack/react-store@0.9.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-store@0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@tanstack/store': 0.9.3 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - use-sync-external-store: 1.6.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.2.6) - '@tanstack/react-virtual@3.14.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tanstack/react-virtual@3.14.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@tanstack/virtual-core': 3.17.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) '@tanstack/router-core@1.171.13': dependencies: @@ -22035,7 +21710,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': + '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2))': dependencies: '@babel/core': 7.29.0 '@babel/template': 7.28.6 @@ -22047,13 +21722,13 @@ snapshots: unplugin: 3.0.0 zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) webpack: 5.105.0(@swc/core@1.15.43)(esbuild@0.27.2) transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2))': + '@tanstack/router-plugin@1.168.18(@tanstack/react-router@1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(webpack@5.105.0(esbuild@0.27.2))': dependencies: '@babel/core': 7.29.0 '@babel/template': 7.28.6 @@ -22065,7 +21740,7 @@ snapshots: unplugin: 3.0.0 zod: 4.4.3 optionalDependencies: - '@tanstack/react-router': 1.170.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@tanstack/react-router': 1.170.15(react-dom@19.2.6(react@19.2.6))(react@19.2.6) vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) webpack: 5.105.0(esbuild@0.27.2) transitivePeerDependencies: @@ -22110,24 +21785,24 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react-native@13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-native@13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: jest-matcher-utils: 30.4.1 picocolors: 1.1.1 pretty-format: 30.4.1 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-test-renderer: 19.1.0(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-test-renderer: 19.2.6(react@19.2.6) redent: 3.0.0 optionalDependencies: jest: 30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)) - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -22279,7 +21954,7 @@ snapshots: prosemirror-transform: 1.11.0 prosemirror-view: 1.41.5 - '@tiptap/react@3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@tiptap/react@3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@tiptap/core': 3.19.0(@tiptap/pm@3.19.0) '@tiptap/pm': 3.19.0 @@ -22287,9 +21962,9 @@ snapshots: '@types/react-dom': 19.2.3(@types/react@19.2.17) '@types/use-sync-external-store': 0.0.6 fast-equals: 5.4.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - use-sync-external-store: 1.6.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: '@tiptap/extension-bubble-menu': 3.19.0(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) '@tiptap/extension-floating-menu': 3.19.0(@floating-ui/dom@1.7.6)(@tiptap/core@3.19.0(@tiptap/pm@3.19.0))(@tiptap/pm@3.19.0) @@ -22355,12 +22030,12 @@ snapshots: dependencies: typescript: 5.9.3 - '@trpc/tanstack-react-query@11.17.0(@tanstack/react-query@5.101.0(react@19.1.0))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.1.0)(typescript@5.9.3)': + '@trpc/tanstack-react-query@11.17.0(@tanstack/react-query@5.101.0(react@19.2.6))(@trpc/client@11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.17.0(typescript@5.9.3))(react@19.2.6)(typescript@5.9.3)': dependencies: - '@tanstack/react-query': 5.101.0(react@19.1.0) + '@tanstack/react-query': 5.101.0(react@19.2.6) '@trpc/client': 11.17.0(@trpc/server@11.17.0(typescript@5.9.3))(typescript@5.9.3) '@trpc/server': 11.17.0(typescript@5.9.3) - react: 19.1.0 + react: 19.2.6 typescript: 5.9.3 '@ts-morph/common@0.27.0': @@ -22483,10 +22158,6 @@ snapshots: dependencies: '@types/node': 24.12.0 - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 24.12.0 - '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -22698,18 +22369,6 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true - '@urql/core@5.2.0(graphql@16.12.0)': - dependencies: - '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) - wonka: 6.3.5 - transitivePeerDependencies: - - graphql - - '@urql/exchange-retry@1.3.2(@urql/core@5.2.0(graphql@16.12.0))': - dependencies: - '@urql/core': 5.2.0(graphql@16.12.0) - wonka: 6.3.5 - '@vitejs/plugin-react@4.7.0(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@25.2.0)(esbuild@0.27.2)(jiti@1.21.7)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 @@ -22909,7 +22568,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@24.12.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) + vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.2.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@25.2.0)(typescript@5.9.3))(vite@7.3.5(@types/node@25.2.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/utils@3.2.4': dependencies: @@ -23093,6 +22752,8 @@ snapshots: agent-base@7.1.4: {} + agent-cli-detector@0.1.4: {} + agent-install@0.0.5: dependencies: '@iarna/toml': 2.2.5 @@ -23159,6 +22820,10 @@ snapshots: dependencies: environment: 1.1.0 + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-regex@4.1.1: {} ansi-regex@5.0.1: {} @@ -23280,14 +22945,6 @@ snapshots: pvutils: 1.1.5 tslib: 2.8.1 - assert@2.1.0: - dependencies: - call-bind: 1.0.9 - is-nan: 1.3.2 - object-is: 1.1.6 - object.assign: 4.1.7 - util: 0.12.5 - assertion-error@2.0.1: {} ast-types@0.16.1: @@ -23304,8 +22961,6 @@ snapshots: async-exit-hook@2.0.1: {} - async-limiter@1.0.1: {} - async@3.2.6: {} asynckit@0.4.0: {} @@ -23317,9 +22972,7 @@ snapshots: stubborn-fs: 2.0.0 when-exit: 2.1.5 - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 + auto-bind@5.0.1: {} await-to-js@3.0.0: {} @@ -23344,20 +22997,7 @@ snapshots: '@babel/core': 7.29.0 '@babel/parser': 7.29.7 '@babel/traverse': 7.29.0 - '@babel/types': 7.29.7 - transitivePeerDependencies: - - supports-color - - babel-jest@29.7.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.29.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -23374,16 +23014,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - babel-plugin-istanbul@7.0.1: dependencies: '@babel/helper-plugin-utils': 7.28.6 @@ -23394,13 +23024,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.7 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.28.0 - babel-plugin-jest-hoist@30.4.0: dependencies: '@types/babel__core': 7.20.5 @@ -23435,9 +23058,13 @@ snapshots: babel-plugin-react-native-web@0.21.2: {} - babel-plugin-syntax-hermes-parser@0.29.1: + babel-plugin-syntax-hermes-parser@0.36.0: + dependencies: + hermes-parser: 0.36.0 + + babel-plugin-syntax-hermes-parser@0.36.1: dependencies: - hermes-parser: 0.29.1 + hermes-parser: 0.36.1 babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0): dependencies: @@ -23464,44 +23091,58 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - babel-preset-expo@54.0.10(@babel/core@7.29.0)(@babel/runtime@7.28.6)(expo@54.0.33)(react-refresh@0.14.2): + babel-preset-expo@57.0.4(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@57.0.8)(react-refresh@0.14.2): dependencies: + '@babel/generator': 7.29.1 '@babel/helper-module-imports': 7.28.6 '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0) + '@react-native/babel-plugin-codegen': 0.86.0(@babel/core@7.29.0) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 - babel-plugin-syntax-hermes-parser: 0.29.1 + babel-plugin-syntax-hermes-parser: 0.36.1 babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) debug: 4.4.3 react-refresh: 0.14.2 - resolve-from: 5.0.0 optionalDependencies: - '@babel/runtime': 7.28.6 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@babel/runtime': 7.29.2 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) transitivePeerDependencies: - '@babel/core' - supports-color - babel-preset-jest@29.6.3(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - babel-preset-jest@30.4.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -23530,10 +23171,6 @@ snapshots: dependencies: safe-buffer: 5.1.2 - better-opn@3.0.2: - dependencies: - open: 8.4.2 - better-sqlite3@12.10.1: dependencies: bindings: 1.5.0 @@ -23554,13 +23191,13 @@ snapshots: dependencies: file-uri-to-path: 1.0.0 - bippy@0.5.42(react@19.1.0): + bippy@0.5.42(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 - bippy@0.5.43(react@19.1.0): + bippy@0.6.1(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 bl@4.1.0: dependencies: @@ -23714,13 +23351,6 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.9: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -23813,14 +23443,13 @@ snapshots: chrome-trace-event@1.0.4: optional: true - chromium-edge-launcher@0.2.0: + chromium-edge-launcher@0.3.0: dependencies: '@types/node': 24.12.0 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 mkdirp: 1.0.4 - rimraf: 3.0.2 transitivePeerDependencies: - supports-color @@ -23844,10 +23473,16 @@ snapshots: clean-stack@2.2.0: {} + cli-boxes@4.0.1: {} + cli-cursor@2.1.0: dependencies: restore-cursor: 2.0.0 + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -23861,6 +23496,11 @@ snapshots: slice-ansi: 5.0.0 string-width: 7.2.0 + cli-truncate@6.1.1: + dependencies: + slice-ansi: 9.0.0 + string-width: 8.2.1 + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -23885,14 +23525,14 @@ snapshots: clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -23901,6 +23541,10 @@ snapshots: code-block-writer@13.0.3: {} + code-excerpt@4.0.0: + dependencies: + convert-to-spaces: 2.0.1 + collect-v8-coverage@1.0.3: {} color-convert@1.9.3: @@ -24021,6 +23665,8 @@ snapshots: convert-source-map@2.0.0: {} + convert-to-spaces@2.0.1: {} + cookie-es@3.1.1: {} cookie-signature@1.2.2: {} @@ -24083,8 +23729,6 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crypto-random-string@2.0.0: {} - css-in-js-utils@3.1.0: dependencies: hyphenate-style-name: 1.1.0 @@ -24241,8 +23885,7 @@ snapshots: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - - define-lazy-prop@2.0.0: {} + optional: true define-lazy-prop@3.0.0: {} @@ -24251,6 +23894,7 @@ snapshots: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 + optional: true delayed-stream@1.0.0: {} @@ -24258,12 +23902,12 @@ snapshots: dequal@2.0.3: {} - deslop-js@0.8.3: + deslop-js@0.9.2: dependencies: - '@oxc-project/types': 0.138.0 + '@oxc-project/types': 0.141.0 fast-glob: 3.3.3 minimatch: 10.2.5 - oxc-parser: 0.138.0 + oxc-parser: 0.141.0 oxc-resolver: 11.24.2 typescript: 5.9.3 @@ -24311,6 +23955,8 @@ snapshots: - electron-builder-squirrel-windows - supports-color + dnssd-advertise@1.1.6: {} + doctrine@3.0.0: dependencies: esutils: 2.0.3 @@ -24568,8 +24214,6 @@ snapshots: entities@6.0.1: {} - env-editor@0.4.2: {} - env-paths@2.2.1: {} env-paths@3.0.0: {} @@ -24605,6 +24249,8 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + es-toolkit@1.50.0: {} + es6-error@4.1.1: {} esbuild-register@3.6.0(esbuild@0.25.12): @@ -24859,8 +24505,6 @@ snapshots: dependencies: eventsource-parser: 3.0.6 - exec-async@2.2.0: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -24925,330 +24569,362 @@ snapshots: jest-mock: 30.4.1 jest-util: 30.4.1 - expo-application@7.0.8(expo@54.0.33): + expo-application@57.0.2(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-asset@12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-asset@57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3): dependencies: - '@expo/image-utils': 0.8.8 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + '@expo/image-utils': 0.11.4(typescript@5.9.3) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: - supports-color + - typescript + + expo-audio@57.0.3(expo-asset@57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3))(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + dependencies: + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-asset: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-auth-session@7.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-auth-session@57.0.5(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo-application: 7.0.8(expo@54.0.33) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - expo-crypto: 15.0.8(expo@54.0.33) - expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-web-browser: 15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-application: 57.0.2(expo@57.0.8) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-crypto: 57.0.1(expo@57.0.8) + expo-linking: 57.0.4(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-web-browser: 57.0.2(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) invariant: 2.2.4 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: - expo - supports-color - expo-av@16.0.8(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): - dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - - expo-camera@55.0.15(@types/emscripten@1.41.5)(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-camera@57.0.3(@types/emscripten@1.41.5)(expo@57.0.8)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: barcode-detector: 3.1.2(@types/emscripten@1.41.5) - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - '@types/emscripten' - expo-clipboard@55.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-clipboard@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-constants@18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): + expo-constants@57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - '@expo/config': 12.0.13 - '@expo/env': 2.0.8 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + '@expo/env': 2.4.2 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: - supports-color - expo-crypto@15.0.8(expo@54.0.33): + expo-crypto@57.0.1(expo@57.0.8): dependencies: - base64-js: 1.5.1 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-dev-client@6.0.20(expo@54.0.33): + expo-dev-client@57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-dev-launcher: 6.0.20(expo@54.0.33) - expo-dev-menu: 7.0.18(expo@54.0.33) - expo-dev-menu-interface: 2.0.0(expo@54.0.33) - expo-manifests: 1.0.10(expo@54.0.33) - expo-updates-interface: 2.0.0(expo@54.0.33) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-dev-launcher: 57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-dev-menu: 57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-dev-menu-interface: 57.0.0(expo@57.0.8) + expo-manifests: 57.0.1(expo@57.0.8) + expo-updates-interface: 57.0.1(expo@57.0.8) transitivePeerDependencies: - - supports-color + - react-native - expo-dev-launcher@6.0.20(expo@54.0.33): + expo-dev-launcher@57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - ajv: 8.20.0 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu: 7.0.18(expo@54.0.33) - expo-manifests: 1.0.10(expo@54.0.33) - transitivePeerDependencies: - - supports-color + '@expo/schema-utils': 57.0.2 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-dev-menu: 57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-manifests: 57.0.1(expo@57.0.8) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-dev-menu-interface@2.0.0(expo@54.0.33): + expo-dev-menu-interface@57.0.0(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-dev-menu@7.0.18(expo@54.0.33): + expo-dev-menu@57.0.9(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu-interface: 2.0.0(expo@54.0.33) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-dev-menu-interface: 57.0.0(expo@57.0.8) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-device@8.0.10(expo@54.0.33): + expo-device@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) ua-parser-js: 0.7.41 - expo-document-picker@14.0.8(expo@54.0.33): + expo-document-picker@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): + expo-file-system@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-font@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) fontfaceobserver: 2.3.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-glass-effect@0.1.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-glass-effect@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-haptics@55.0.14(expo@54.0.33): + expo-haptics@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-image-loader@6.0.0(expo@54.0.33): + expo-image-loader@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-image-picker@17.0.11(expo@54.0.33): + expo-image-picker@57.0.6(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-image-loader: 6.0.0(expo@54.0.33) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-image-loader: 57.0.1(expo@57.0.8) - expo-json-utils@0.15.0: {} + expo-json-utils@57.0.1: {} - expo-keep-awake@15.0.8(expo@54.0.33)(react@19.1.0): + expo-keep-awake@57.0.1(expo@57.0.8)(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 - expo-linear-gradient@15.0.8(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-linear-gradient@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-linking@8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-linking@57.0.4(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) invariant: 2.2.4 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: - expo - supports-color - expo-localization@17.0.8(expo@54.0.33)(react@19.1.0): + expo-localization@57.0.1(expo@57.0.8)(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 rtl-detect: 1.1.2 - expo-manifests@1.0.10(expo@54.0.33): + expo-manifests@57.0.1(expo@57.0.8): dependencies: - '@expo/config': 12.0.13 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-json-utils: 0.15.0 - transitivePeerDependencies: - - supports-color + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-json-utils: 57.0.1 - expo-modules-autolinking@3.0.24: + expo-modules-autolinking@57.0.9(typescript@5.9.3): dependencies: - '@expo/spawn-async': 1.7.2 + '@expo/require-utils': 57.0.4(typescript@5.9.3) + '@expo/spawn-async': 1.8.0 chalk: 4.1.2 commander: 7.2.0 - require-from-string: 2.0.2 - resolve-from: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript - expo-modules-core@3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-modules-core@57.0.7(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: + '@expo/expo-modules-macros-plugin': 0.6.1 + expo-modules-jsi: 57.0.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) invariant: 2.2.4 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + optionalDependencies: + react-native-worklets: 0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + + expo-modules-jsi@57.0.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): + dependencies: + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-notifications@0.32.17(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-notifications@57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3): dependencies: - '@expo/image-utils': 0.8.8 - '@ide/backoff': 1.0.0 + '@expo/image-utils': 0.11.4(typescript@5.9.3) abort-controller: 3.0.0 - assert: 2.1.0 badgin: 1.2.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-application: 7.0.8(expo@54.0.33) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-application: 57.0.2(expo@57.0.8) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) transitivePeerDependencies: - supports-color + - typescript - expo-router@6.0.23(76047f2336d892e43bef2ac48cb56303): + expo-router@57.0.8(507433f30d6a233882de5eb4189dda28): dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@expo/schema-utils': 0.1.8 - '@radix-ui/react-slot': 1.2.0(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-navigation/bottom-tabs': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native-stack': 7.12.0(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro-runtime': 57.0.7(@expo/log-box@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/schema-utils': 57.0.2 + '@expo/ui': 57.0.7(@babel/core@7.29.0)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@testing-library/jest-dom': 6.9.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) client-only: 0.0.1 + color: 4.2.3 debug: 4.4.3 escape-string-regexp: 4.0.0 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - expo-linking: 8.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-server: 1.0.5 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-glass-effect: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-linking: 57.0.4(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-server: 57.0.1 + expo-symbols: 57.0.1(expo-font@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) fast-deep-equal: 3.1.3 invariant: 2.2.4 - nanoid: 3.3.11 + nanoid: 3.3.12 query-string: 7.1.3 - react: 19.1.0 + react: 19.2.6 react-fast-compare: 3.2.2 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - semver: 7.6.3 + react-is: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-drawer-layout: 4.2.9(react-native-gesture-handler@3.1.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-screens: 4.26.2(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) server-only: 0.0.1 sf-symbols-typescript: 2.2.0 shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.1.0) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + standard-navigation: 0.0.5 + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) optionalDependencies: - '@testing-library/react-native': 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) - react-dom: 19.1.0(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@30.4.2(@types/node@25.2.0)(esbuild-register@3.6.0(esbuild@0.27.2)))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react-test-renderer@19.2.6(react@19.2.6))(react@19.2.6) + react-dom: 19.2.6(react@19.2.6) + react-native-gesture-handler: 3.1.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - - '@react-native-masked-view/masked-view' + - '@babel/core' + - '@testing-library/dom' - '@types/react' - '@types/react-dom' + - expo-font + - react-native-worklets - supports-color - expo-secure-store@15.0.8(expo@54.0.33): + expo-secure-store@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-server@1.0.5: {} + expo-server@57.0.1: {} - expo-speech-recognition@3.1.2(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-speech-recognition@56.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-splash-screen@31.0.13(expo@54.0.33): + expo-splash-screen@57.0.5(expo@57.0.8)(typescript@5.9.3): dependencies: - '@expo/prebuild-config': 54.0.8(expo@54.0.33) - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@expo/config-plugins': 57.0.6(typescript@5.9.3) + '@expo/image-utils': 0.11.4(typescript@5.9.3) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + xml2js: 0.6.0 transitivePeerDependencies: - supports-color + - typescript + + expo-status-bar@57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + dependencies: + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo-status-bar@3.0.9(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo-symbols@57.0.1(expo-font@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@expo-google-fonts/material-symbols': 0.4.42 + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-font: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + sf-symbols-typescript: 2.2.0 - expo-system-ui@6.0.9(expo@54.0.33)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): + expo-system-ui@57.0.1(expo@57.0.8)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - '@react-native/normalize-colors': 0.81.5 + '@react-native/normalize-colors': 0.86.0 debug: 4.4.3 - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) optionalDependencies: - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - supports-color - expo-updates-interface@2.0.0(expo@54.0.33): + expo-updates-interface@57.0.1(expo@57.0.8): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) - expo-web-browser@15.0.10(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)): + expo-web-browser@57.0.2(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)): dependencies: - expo: 54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + expo: 57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - expo@54.0.33(@babel/core@7.29.0)(@expo/metro-runtime@6.1.2)(expo-router@6.0.23)(graphql@16.12.0)(react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + expo@57.0.8(@babel/core@7.29.0)(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-router@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3): dependencies: - '@babel/runtime': 7.28.6 - '@expo/cli': 54.0.23(expo-router@6.0.23)(expo@54.0.33)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 - '@expo/devtools': 0.1.8(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - '@expo/fingerprint': 0.15.4 - '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.14(expo@54.0.33) - '@expo/vector-icons': 15.0.3(expo-font@14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@babel/runtime': 7.29.2 + '@expo/cli': 57.0.10(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.7)(expo-constants@57.0.7)(expo-font@57.0.1)(expo-router@57.0.8)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + '@expo/config': 57.0.6(typescript@5.9.3) + '@expo/config-plugins': 57.0.6(typescript@5.9.3) + '@expo/devtools': 57.0.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/fingerprint': 0.20.6 + '@expo/local-build-cache-provider': 57.0.4(typescript@5.9.3) + '@expo/log-box': 57.0.1(@expo/dom-webview@57.0.1)(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro': 56.0.0 + '@expo/metro-config': 57.0.7(expo@57.0.8)(typescript@5.9.3) '@ungap/structured-clone': 1.3.0 - babel-preset-expo: 54.0.10(@babel/core@7.29.0)(@babel/runtime@7.28.6)(expo@54.0.33)(react-refresh@0.14.2) - expo-asset: 12.0.12(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - expo-font: 14.0.11(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-keep-awake: 15.0.8(expo@54.0.33)(react@19.1.0) - expo-modules-autolinking: 3.0.24 - expo-modules-core: 3.0.29(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + babel-preset-expo: 57.0.4(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@57.0.8)(react-refresh@0.14.2) + expo-asset: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(typescript@5.9.3) + expo-constants: 57.0.7(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-file-system: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-font: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-keep-awake: 57.0.1(expo@57.0.8)(react@19.2.6) + expo-modules-autolinking: 57.0.9(typescript@5.9.3) + expo-modules-core: 57.0.7(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) pretty-format: 29.7.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) react-refresh: 0.14.2 - whatwg-url-without-unicode: 8.0.0-3 + whatwg-url-minimum: 0.1.2 optionalDependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.33)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-webview: 13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@expo/dom-webview': 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + '@expo/metro-runtime': 57.0.7(@expo/log-box@57.0.1)(expo@57.0.8)(react-dom@19.2.6(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-dom: 19.2.6(react@19.2.6) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react-native-webview: 13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - '@babel/core' - bufferutil - expo-router - - graphql + - expo-widgets + - react-native-worklets + - react-server-dom-webpack - supports-color + - typescript - utf-8-validate exponential-backoff@3.1.3: {} @@ -25317,6 +24993,8 @@ snapshots: dependencies: reusify: 1.1.0 + fb-dotslash@0.5.8: {} + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -25343,11 +25021,17 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fetch-nodeshim@0.4.10: {} + fflate@0.4.8: {} fflate@0.8.2: {} @@ -25460,10 +25144,6 @@ snapshots: fontfaceobserver@2.3.0: {} - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@2.0.0: dependencies: cross-spawn: 7.0.6 @@ -25492,16 +25172,14 @@ snapshots: forwarded@0.2.0: {} - framer-motion@12.31.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + framer-motion@12.31.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: motion-dom: 12.30.1 motion-utils: 12.29.2 tslib: 2.8.1 optionalDependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - - freeport-async@2.0.0: {} + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) fresh@0.5.2: {} @@ -25576,8 +25254,6 @@ snapshots: transitivePeerDependencies: - supports-color - generator-function@2.0.1: {} - gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -25698,10 +25374,6 @@ snapshots: serialize-error: 7.0.1 optional: true - global-dirs@0.1.1: - dependencies: - ini: 1.3.8 - global-modules@0.2.3: dependencies: global-prefix: 0.1.5 @@ -25770,6 +25442,7 @@ snapshots: has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 + optional: true has-symbols@1.1.0: {} @@ -25883,23 +25556,31 @@ snapshots: headers-polyfill@4.0.3: {} + hermes-compiler@250829098.0.14: {} + hermes-estree@0.25.1: {} - hermes-estree@0.29.1: {} + hermes-estree@0.35.0: {} - hermes-estree@0.32.0: {} + hermes-estree@0.36.0: {} + + hermes-estree@0.36.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 - hermes-parser@0.29.1: + hermes-parser@0.35.0: + dependencies: + hermes-estree: 0.35.0 + + hermes-parser@0.36.0: dependencies: - hermes-estree: 0.29.1 + hermes-estree: 0.36.0 - hermes-parser@0.32.0: + hermes-parser@0.36.1: dependencies: - hermes-estree: 0.32.0 + hermes-estree: 0.36.1 highlight.js@10.7.3: {} @@ -26062,6 +25743,8 @@ snapshots: indent-string@4.0.0: {} + indent-string@5.0.0: {} + inflight@1.0.6: dependencies: once: 1.4.0 @@ -26071,6 +25754,47 @@ snapshots: ini@1.3.8: {} + ink-spinner@5.0.0(ink@7.1.1(@types/react@19.2.17)(react-devtools-core@6.1.5)(react@19.2.6))(react@19.2.6): + dependencies: + cli-spinners: 2.9.2 + ink: 7.1.1(@types/react@19.2.17)(react-devtools-core@6.1.5)(react@19.2.6) + react: 19.2.6 + + ink@7.1.1(@types/react@19.2.17)(react-devtools-core@6.1.5)(react@19.2.6): + dependencies: + '@alcalzone/ansi-tokenize': 0.3.0 + ansi-escapes: 7.3.0 + ansi-styles: 6.2.3 + auto-bind: 5.0.1 + chalk: 5.6.2 + cli-boxes: 4.0.1 + cli-cursor: 4.0.0 + cli-truncate: 6.1.1 + code-excerpt: 4.0.0 + es-toolkit: 1.50.0 + indent-string: 5.0.0 + is-in-ci: 2.0.0 + patch-console: 2.0.0 + react: 19.2.6 + react-reconciler: 0.33.0(react@19.2.6) + scheduler: 0.27.0 + signal-exit: 3.0.7 + slice-ansi: 9.0.0 + stack-utils: 2.0.6 + string-width: 8.2.1 + terminal-size: 4.0.1 + type-fest: 5.6.0 + widest-line: 6.0.0 + wrap-ansi: 10.0.0 + ws: 8.21.1 + yoga-layout: 3.2.1 + optionalDependencies: + '@types/react': 19.2.17 + react-devtools-core: 6.1.5 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + inline-style-parser@0.2.7: {} inline-style-prefixer@7.0.1: @@ -26102,11 +25826,6 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-arguments@1.2.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-arrayish@0.2.1: {} is-arrayish@0.3.4: {} @@ -26115,8 +25834,6 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-callable@1.2.7: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -26139,20 +25856,14 @@ snapshots: is-generator-fn@2.1.0: {} - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - 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@2.0.1: {} + is-in-ci@2.0.0: {} + is-in-ssh@1.0.0: {} is-inside-container@1.0.0: @@ -26161,11 +25872,6 @@ snapshots: is-interactive@2.0.0: {} - is-nan@1.3.2: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - is-node-process@1.2.0: {} is-number@7.0.0: {} @@ -26180,13 +25886,6 @@ snapshots: is-promise@4.0.0: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - is-regexp@3.1.0: {} is-ssh@1.4.1: @@ -26199,10 +25898,6 @@ snapshots: is-stream@4.0.1: {} - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.20 - is-typedarray@1.0.0: {} is-unicode-supported@1.3.0: {} @@ -26254,16 +25949,6 @@ snapshots: transitivePeerDependencies: - supports-color - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.7 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 @@ -26477,15 +26162,6 @@ snapshots: jest-util: 30.4.1 pretty-format: 30.4.1 - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 24.12.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - jest-environment-node@30.4.1: dependencies: '@jest/environment': 30.4.1 @@ -26498,22 +26174,6 @@ snapshots: jest-get-type@29.6.3: {} - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 24.12.0 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - jest-haste-map@30.4.1: dependencies: '@jest/types': 30.4.1 @@ -26524,7 +26184,7 @@ snapshots: jest-regex-util: 30.4.0 jest-util: 30.4.1 jest-worker: 30.4.1 - picomatch: 4.0.3 + picomatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 @@ -26560,18 +26220,6 @@ snapshots: jest-diff: 30.4.1 pretty-format: 30.4.1 - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.29.0 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - jest-message-util@30.4.1: dependencies: '@babel/code-frame': 7.29.0 @@ -26580,17 +26228,11 @@ snapshots: chalk: 4.1.2 graceful-fs: 4.2.11 jest-util: 30.4.1 - picomatch: 4.0.3 + picomatch: 4.0.5 pretty-format: 30.4.1 slash: 3.0.0 stack-utils: 2.0.6 - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 24.12.0 - jest-util: 29.7.0 - jest-mock@30.4.1: dependencies: '@jest/types': 30.4.1 @@ -26617,8 +26259,6 @@ snapshots: - debug - supports-color - jest-regex-util@29.6.3: {} - jest-regex-util@30.4.0: {} jest-resolve-dependencies@30.4.2: @@ -26739,7 +26379,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.3 + picomatch: 4.0.5 jest-validate@29.7.0: dependencies: @@ -27017,7 +26657,7 @@ snapshots: typescript: 6.0.3 zod: 4.4.3 - lan-network@0.1.7: {} + lan-network@0.2.1: {} launch-editor@2.14.1: dependencies: @@ -27040,61 +26680,40 @@ snapshots: transitivePeerDependencies: - supports-color - lightningcss-android-arm64@1.31.1: - optional: true - lightningcss-android-arm64@1.32.0: optional: true lightningcss-darwin-arm64@1.27.0: optional: true - lightningcss-darwin-arm64@1.31.1: - optional: true - lightningcss-darwin-arm64@1.32.0: optional: true lightningcss-darwin-x64@1.27.0: optional: true - lightningcss-darwin-x64@1.31.1: - optional: true - lightningcss-darwin-x64@1.32.0: optional: true lightningcss-freebsd-x64@1.27.0: optional: true - lightningcss-freebsd-x64@1.31.1: - optional: true - lightningcss-freebsd-x64@1.32.0: optional: true lightningcss-linux-arm-gnueabihf@1.27.0: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: - optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: optional: true lightningcss-linux-arm64-gnu@1.27.0: optional: true - lightningcss-linux-arm64-gnu@1.31.1: - optional: true - lightningcss-linux-arm64-gnu@1.32.0: optional: true - lightningcss-linux-arm64-musl@1.27.0: - optional: true - - lightningcss-linux-arm64-musl@1.31.1: + lightningcss-linux-arm64-musl@1.27.0: optional: true lightningcss-linux-arm64-musl@1.32.0: @@ -27103,36 +26722,24 @@ snapshots: lightningcss-linux-x64-gnu@1.27.0: optional: true - lightningcss-linux-x64-gnu@1.31.1: - optional: true - lightningcss-linux-x64-gnu@1.32.0: optional: true lightningcss-linux-x64-musl@1.27.0: optional: true - lightningcss-linux-x64-musl@1.31.1: - optional: true - lightningcss-linux-x64-musl@1.32.0: optional: true lightningcss-win32-arm64-msvc@1.27.0: optional: true - lightningcss-win32-arm64-msvc@1.31.1: - optional: true - lightningcss-win32-arm64-msvc@1.32.0: optional: true lightningcss-win32-x64-msvc@1.27.0: optional: true - lightningcss-win32-x64-msvc@1.31.1: - optional: true - lightningcss-win32-x64-msvc@1.32.0: optional: true @@ -27151,22 +26758,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.27.0 lightningcss-win32-x64-msvc: 1.27.0 - lightningcss@1.31.1: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -27310,13 +26901,13 @@ snapshots: lru_map@0.4.1: {} - lucide-react@0.577.0(react@19.1.0): + lucide-react@0.577.0(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 - lucide-react@1.7.0(react@19.1.0): + lucide-react@1.7.0(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 lz-string@1.5.0: {} @@ -27562,50 +27153,51 @@ snapshots: meriyah@6.1.4: {} - metro-babel-transformer@0.83.3: + metro-babel-transformer@0.84.4: dependencies: '@babel/core': 7.29.0 flow-enums-runtime: 0.0.6 - hermes-parser: 0.32.0 + hermes-parser: 0.35.0 + metro-cache-key: 0.84.4 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-cache-key@0.83.3: + metro-cache-key@0.84.4: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.83.3: + metro-cache@0.84.4: dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 https-proxy-agent: 7.0.6 - metro-core: 0.83.3 + metro-core: 0.84.4 transitivePeerDependencies: - supports-color - metro-config@0.83.3: + metro-config@0.84.4: dependencies: connect: 3.7.0 flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.83.3 - metro-cache: 0.83.3 - metro-core: 0.83.3 - metro-runtime: 0.83.3 + metro: 0.84.4 + metro-cache: 0.84.4 + metro-core: 0.84.4 + metro-runtime: 0.84.4 yaml: 2.9.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro-core@0.83.3: + metro-core@0.84.4: dependencies: flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 - metro-resolver: 0.83.3 + metro-resolver: 0.84.4 - metro-file-map@0.83.3: + metro-file-map@0.84.4: dependencies: debug: 4.4.3 fb-watchman: 2.0.2 @@ -27619,47 +27211,46 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.83.3: + metro-minify-terser@0.84.4: dependencies: flow-enums-runtime: 0.0.6 terser: 5.46.0 - metro-resolver@0.83.3: + metro-resolver@0.84.4: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.83.3: + metro-runtime@0.84.4: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 flow-enums-runtime: 0.0.6 - metro-source-map@0.83.3: + metro-source-map@0.84.4: dependencies: '@babel/traverse': 7.29.0 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.29.0' '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.83.3 + metro-symbolicate: 0.84.4 nullthrows: 1.1.1 - ob1: 0.83.3 + ob1: 0.84.4 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.83.3: + metro-symbolicate@0.84.4: dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.83.3 + metro-source-map: 0.84.4 nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.83.3: + metro-transform-plugins@0.84.4: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 @@ -27670,27 +27261,27 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.3: + metro-transform-worker@0.84.4: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 '@babel/parser': 7.29.7 '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-minify-terser: 0.83.3 - metro-source-map: 0.83.3 - metro-transform-plugins: 0.83.3 + metro: 0.84.4 + metro-babel-transformer: 0.84.4 + metro-cache: 0.84.4 + metro-cache-key: 0.84.4 + metro-minify-terser: 0.84.4 + metro-source-map: 0.84.4 + metro-transform-plugins: 0.84.4 nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.83.3: + metro@0.84.4: dependencies: '@babel/code-frame': 7.29.0 '@babel/core': 7.29.0 @@ -27699,33 +27290,32 @@ snapshots: '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.7 - accepts: 1.3.8 - chalk: 4.1.2 + accepts: 2.0.0 ci-info: 2.0.0 connect: 3.7.0 debug: 4.4.3 error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 - hermes-parser: 0.32.0 + hermes-parser: 0.35.0 image-size: 1.2.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-config: 0.83.3 - metro-core: 0.83.3 - metro-file-map: 0.83.3 - metro-resolver: 0.83.3 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 - mime-types: 2.1.35 + metro-babel-transformer: 0.84.4 + metro-cache: 0.84.4 + metro-cache-key: 0.84.4 + metro-config: 0.84.4 + metro-core: 0.84.4 + metro-file-map: 0.84.4 + metro-resolver: 0.84.4 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4 + metro-symbolicate: 0.84.4 + metro-transform-plugins: 0.84.4 + metro-transform-worker: 0.84.4 + mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 source-map: 0.5.7 @@ -28132,6 +27722,8 @@ snapshots: transitivePeerDependencies: - '@types/node' + multitars@1.0.0: {} + mute-stream@2.0.0: {} mz@2.7.0: @@ -28140,19 +27732,17 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} - nanoid@3.3.12: {} napi-build-utils@2.0.0: {} napi-postinstall@0.3.4: {} - nativewind@4.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): + nativewind@4.2.1(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): dependencies: comment-json: 4.5.1 debug: 4.4.3 - react-native-css-interop: 0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) + react-native-css-interop: 0.2.1(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)) tailwindcss: 3.4.19(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - react @@ -28173,8 +27763,6 @@ snapshots: neo-async@2.6.2: optional: true - nested-error-stacks@2.0.1: {} - no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -28303,7 +27891,7 @@ snapshots: transitivePeerDependencies: - supports-color - ob1@0.83.3: + ob1@0.84.4: dependencies: flow-enums-runtime: 0.0.6 @@ -28313,24 +27901,11 @@ snapshots: object-inspect@1.13.4: {} - object-is@1.1.6: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - - object-keys@1.1.1: {} + object-keys@1.1.1: + optional: true object-treeify@1.1.33: {} - object.assign@4.1.7: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - obug@2.1.1: {} omggif@1.0.10: {} @@ -28394,15 +27969,9 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 - - openai@6.26.0(ws@8.19.0)(zod@4.4.3): + openai@6.26.0(ws@8.21.1)(zod@4.4.3): optionalDependencies: - ws: 8.19.0 + ws: 8.21.1 zod: 4.4.3 openapi-types@12.1.3: {} @@ -28513,30 +28082,30 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.127.0 '@oxc-parser/binding-win32-x64-msvc': 0.127.0 - oxc-parser@0.138.0: + oxc-parser@0.141.0: dependencies: - '@oxc-project/types': 0.138.0 + '@oxc-project/types': 0.141.0 optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.138.0 - '@oxc-parser/binding-android-arm64': 0.138.0 - '@oxc-parser/binding-darwin-arm64': 0.138.0 - '@oxc-parser/binding-darwin-x64': 0.138.0 - '@oxc-parser/binding-freebsd-x64': 0.138.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.138.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.138.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.138.0 - '@oxc-parser/binding-linux-arm64-musl': 0.138.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.138.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.138.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.138.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.138.0 - '@oxc-parser/binding-linux-x64-gnu': 0.138.0 - '@oxc-parser/binding-linux-x64-musl': 0.138.0 - '@oxc-parser/binding-openharmony-arm64': 0.138.0 - '@oxc-parser/binding-wasm32-wasi': 0.138.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.138.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.138.0 - '@oxc-parser/binding-win32-x64-msvc': 0.138.0 + '@oxc-parser/binding-android-arm-eabi': 0.141.0 + '@oxc-parser/binding-android-arm64': 0.141.0 + '@oxc-parser/binding-darwin-arm64': 0.141.0 + '@oxc-parser/binding-darwin-x64': 0.141.0 + '@oxc-parser/binding-freebsd-x64': 0.141.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.141.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.141.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.141.0 + '@oxc-parser/binding-linux-arm64-musl': 0.141.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.141.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.141.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-gnu': 0.141.0 + '@oxc-parser/binding-linux-x64-musl': 0.141.0 + '@oxc-parser/binding-openharmony-arm64': 0.141.0 + '@oxc-parser/binding-wasm32-wasi': 0.141.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.141.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.141.0 + '@oxc-parser/binding-win32-x64-msvc': 0.141.0 oxc-resolver@11.17.0: optionalDependencies: @@ -28629,34 +28198,35 @@ snapshots: '@oxfmt/binding-win32-ia32-msvc': 0.45.0 '@oxfmt/binding-win32-x64-msvc': 0.45.0 - oxlint-plugin-react-doctor@0.8.3: + oxlint-plugin-react-doctor@0.9.2: dependencies: + '@shaderfrog/glsl-parser': 7.0.1 '@typescript-eslint/types': 8.62.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 - oxc-parser: 0.138.0 + oxc-parser: 0.141.0 - oxlint@1.66.0: + oxlint@1.74.0: optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.66.0 - '@oxlint/binding-android-arm64': 1.66.0 - '@oxlint/binding-darwin-arm64': 1.66.0 - '@oxlint/binding-darwin-x64': 1.66.0 - '@oxlint/binding-freebsd-x64': 1.66.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.66.0 - '@oxlint/binding-linux-arm-musleabihf': 1.66.0 - '@oxlint/binding-linux-arm64-gnu': 1.66.0 - '@oxlint/binding-linux-arm64-musl': 1.66.0 - '@oxlint/binding-linux-ppc64-gnu': 1.66.0 - '@oxlint/binding-linux-riscv64-gnu': 1.66.0 - '@oxlint/binding-linux-riscv64-musl': 1.66.0 - '@oxlint/binding-linux-s390x-gnu': 1.66.0 - '@oxlint/binding-linux-x64-gnu': 1.66.0 - '@oxlint/binding-linux-x64-musl': 1.66.0 - '@oxlint/binding-openharmony-arm64': 1.66.0 - '@oxlint/binding-win32-arm64-msvc': 1.66.0 - '@oxlint/binding-win32-ia32-msvc': 1.66.0 - '@oxlint/binding-win32-x64-msvc': 1.66.0 + '@oxlint/binding-android-arm-eabi': 1.74.0 + '@oxlint/binding-android-arm64': 1.74.0 + '@oxlint/binding-darwin-arm64': 1.74.0 + '@oxlint/binding-darwin-x64': 1.74.0 + '@oxlint/binding-freebsd-x64': 1.74.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.74.0 + '@oxlint/binding-linux-arm-musleabihf': 1.74.0 + '@oxlint/binding-linux-arm64-gnu': 1.74.0 + '@oxlint/binding-linux-arm64-musl': 1.74.0 + '@oxlint/binding-linux-ppc64-gnu': 1.74.0 + '@oxlint/binding-linux-riscv64-gnu': 1.74.0 + '@oxlint/binding-linux-riscv64-musl': 1.74.0 + '@oxlint/binding-linux-s390x-gnu': 1.74.0 + '@oxlint/binding-linux-x64-gnu': 1.74.0 + '@oxlint/binding-linux-x64-musl': 1.74.0 + '@oxlint/binding-openharmony-arm64': 1.74.0 + '@oxlint/binding-win32-arm64-msvc': 1.74.0 + '@oxlint/binding-win32-ia32-msvc': 1.74.0 + '@oxlint/binding-win32-x64-msvc': 1.74.0 p-cancelable@2.1.1: {} @@ -28759,16 +28329,18 @@ snapshots: partial-json@0.1.7: {} - pastable@2.2.1(react@19.1.0): + pastable@2.2.1(react@19.2.6): dependencies: '@babel/core': 7.29.0 ts-toolbelt: 9.6.0 type-fest: 3.13.1 optionalDependencies: - react: 19.1.0 + react: 19.2.6 transitivePeerDependencies: - supports-color + patch-console@2.0.0: {} + path-browserify@1.0.1: {} path-dirname@1.0.2: {} @@ -28805,20 +28377,20 @@ snapshots: pe-library@0.4.1: {} - phosphor-react-native@3.0.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + phosphor-react-native@3.0.3(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@3.0.1: {} - picomatch@4.0.3: {} + picomatch@4.0.5: {} + pidtree@0.6.0: {} pify@2.3.0: {} @@ -28890,8 +28462,6 @@ snapshots: transitivePeerDependencies: - supports-color - possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.15): dependencies: postcss: 8.5.15 @@ -28948,12 +28518,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.49: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.15: dependencies: nanoid: 3.3.12 @@ -28977,24 +28541,24 @@ snapshots: optionalDependencies: rxjs: 7.8.2 - posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + posthog-react-native-session-replay@1.6.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - posthog-react-native@4.30.0(@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(@react-navigation/native@7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(expo-application@7.0.8(expo@54.0.33))(expo-device@8.0.10(expo@54.0.33))(expo-file-system@19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)))(expo-localization@17.0.8(expo@54.0.33)(react@19.1.0))(posthog-react-native-session-replay@1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)): + posthog-react-native@4.30.0(09aed2ab7f1f7ef1818c91ffd64580ed): dependencies: '@posthog/core': 1.20.0 - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) optionalDependencies: - '@react-native-async-storage/async-storage': 2.2.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - '@react-navigation/native': 7.1.28(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - expo-application: 7.0.8(expo@54.0.33) - expo-device: 8.0.10(expo@54.0.33) - expo-file-system: 19.0.21(expo@54.0.33)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0)) - expo-localization: 17.0.8(expo@54.0.33)(react@19.1.0) - posthog-react-native-session-replay: 1.6.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-native-async-storage/async-storage': 2.2.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + '@react-navigation/native': 7.1.28(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + expo-application: 57.0.2(expo@57.0.8) + expo-device: 57.0.1(expo@57.0.8) + expo-file-system: 57.0.1(expo@57.0.8)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6)) + expo-localization: 57.0.1(expo@57.0.8)(react@19.2.6) + posthog-react-native-session-replay: 1.6.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) postject@1.0.0-alpha.6: dependencies: @@ -29028,8 +28592,6 @@ snapshots: prettier@3.8.1: {} - pretty-bytes@5.6.0: {} - pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -29241,8 +28803,6 @@ snapshots: pvutils@1.1.5: {} - qrcode-terminal@0.11.0: {} - qs@6.15.0: dependencies: side-channel: 1.1.0 @@ -29266,65 +28826,65 @@ snapshots: radix-themes-tw@0.2.3: {} - radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-accordion': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-avatar': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-form': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-label': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-menubar': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-progress': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-select': 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-separator': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slider': 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-switch': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.17)(react@19.2.6) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 '@types/react-dom': 19.2.3(@types/react@19.2.17) @@ -29352,7 +28912,7 @@ snapshots: react-devtools-core@6.1.5: dependencies: - shell-quote: 1.8.3 + shell-quote: 1.9.0 ws: 7.5.10 transitivePeerDependencies: - bufferutil @@ -29377,20 +28937,25 @@ snapshots: transitivePeerDependencies: - supports-color - react-doctor@0.8.3(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)): + react-doctor@0.9.2(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(eslint@10.5.0(jiti@2.7.0))(react-devtools-core@6.1.5): dependencies: '@babel/code-frame': 7.29.0 '@sentry/node': 10.61.0(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1)) agent-install: 0.0.5 conf: 15.1.0 confbox: 0.2.4 - deslop-js: 0.8.3 + deslop-js: 0.9.2 eslint-plugin-react-hooks: 7.1.1(eslint@10.5.0(jiti@2.7.0)) + figures: 6.1.0 + ink: 7.1.1(@types/react@19.2.17)(react-devtools-core@6.1.5)(react@19.2.6) + ink-spinner: 5.0.0(ink@7.1.1(@types/react@19.2.17)(react-devtools-core@6.1.5)(react@19.2.6))(react@19.2.6) jiti: 2.7.0 magicast: 0.5.3 - oxlint: 1.66.0 - oxlint-plugin-react-doctor: 0.8.3 + oxc-resolver: 11.24.2 + oxlint: 1.74.0 + oxlint-plugin-react-doctor: 0.9.2 prompts: 2.4.2 + react: 19.2.6 typescript: 5.9.3 vscode-languageserver: 9.0.1 vscode-languageserver-textdocument: 1.0.12 @@ -29399,14 +28964,14 @@ snapshots: transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' + - '@types/react' + - bufferutil - eslint - oxlint-tsgolint + - react-devtools-core - supports-color - - react-dom@19.1.0(react@19.1.0): - dependencies: - react: 19.1.0 - scheduler: 0.26.0 + - utf-8-validate + - vite-plus react-dom@19.2.6(react@19.2.6): dependencies: @@ -29415,21 +28980,21 @@ snapshots: react-fast-compare@3.2.2: {} - react-freeze@1.0.4(react@19.1.0): + react-freeze@1.0.4(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 - react-grab@0.1.48(react@19.1.0): + react-grab@0.1.50(react@19.2.6): dependencies: - '@react-grab/cli': 0.1.48 - bippy: 0.5.43(react@19.1.0) + '@react-grab/cli': 0.1.50 + bippy: 0.6.1(react@19.2.6) optionalDependencies: - react: 19.1.0 + react: 19.2.6 - react-hotkeys-hook@4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-hotkeys-hook@4.6.2(patch_hash=a8cd00b963d4ae6787cc267a2345cc1b32bcba5fa1131328b8017ee0d316ab0e)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) react-is@16.13.1: {} @@ -29439,7 +29004,7 @@ snapshots: react-is@19.2.6: {} - react-markdown@10.1.0(@types/react@19.2.17)(react@19.1.0): + react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.6): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -29448,7 +29013,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 - react: 19.1.0 + react: 19.2.6 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -29457,79 +29022,98 @@ snapshots: transitivePeerDependencies: - supports-color - react-native-css-interop@0.2.1(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): + react-native-css-interop@0.2.1(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6)(tailwindcss@3.4.19(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@babel/helper-module-imports': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.7 debug: 4.4.3 lightningcss: 1.27.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) semver: 7.8.4 tailwindcss: 3.4.19(tsx@4.22.4)(yaml@2.9.0) optionalDependencies: - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - supports-color - react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-drawer-layout@4.2.9(react-native-gesture-handler@3.1.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + color: 4.2.3 + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-gesture-handler: 3.1.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + use-latest-callback: 0.2.6(react@19.2.6) + + react-native-gesture-handler@3.1.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + dependencies: + '@types/react-test-renderer': 19.1.0 + invariant: 2.2.4 + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-is-edge-to-edge@1.2.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - react-native-reanimated@4.1.6(@babel/core@7.29.0)(react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-is-edge-to-edge@1.3.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - '@babel/core': 7.29.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - react-native-worklets: 0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) - semver: 7.7.2 + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + + react-native-keyboard-controller@1.21.9(react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + dependencies: + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-is-edge-to-edge: 1.2.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-reanimated: 4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + + react-native-reanimated@4.5.0(react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): + dependencies: + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-is-edge-to-edge: 1.3.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + react-native-worklets: 0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) + semver: 7.8.4 - react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-safe-area-context@5.7.0(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-screens@4.26.2(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-freeze: 1.0.4(react@19.1.0) - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react: 19.2.6 + react-freeze: 1.0.4(react@19.2.6) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) warn-once: 0.1.1 - react-native-svg-transformer@1.5.3(react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(typescript@5.9.3): + react-native-svg-transformer@1.5.3(react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(typescript@5.9.3): dependencies: '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) path-dirname: 1.0.2 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - react-native-svg: 15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + react-native-svg: 15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) transitivePeerDependencies: - supports-color - typescript - react-native-svg@15.15.2(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-svg@15.15.4(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: css-select: 5.2.2 css-tree: 1.1.3 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) warn-once: 0.1.1 - react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: '@babel/runtime': 7.28.6 '@react-native/normalize-colors': 0.74.89 @@ -29538,74 +29122,74 @@ snapshots: memoize-one: 6.0.0 nullthrows: 1.1.1 postcss-value-parser: 4.2.0 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) styleq: 0.1.3 transitivePeerDependencies: - encoding - react-native-webview@13.16.0(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-webview@13.16.1(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) - react-native-worklets@0.7.2(@babel/core@7.29.0)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0): + react-native-worklets@0.10.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6): dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/preset-typescript': 7.27.1(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@babel/types': 7.29.7 + '@react-native/metro-config': 0.86.0(@babel/core@7.29.0) convert-source-map: 2.0.0 - react: 19.1.0 - react-native: 0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0) - semver: 7.7.3 + react: 19.2.6 + react-native: 0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6) + semver: 7.8.4 transitivePeerDependencies: - supports-color - react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0): + react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6): dependencies: - '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.81.5 - '@react-native/codegen': 0.81.5(@babel/core@7.29.0) - '@react-native/community-cli-plugin': 0.81.5 - '@react-native/gradle-plugin': 0.81.5 - '@react-native/js-polyfills': 0.81.5 - '@react-native/normalize-colors': 0.81.5 - '@react-native/virtualized-lists': 0.81.5(@types/react@19.2.17)(react-native@0.81.5(@babel/core@7.29.0)(@types/react@19.2.17)(react@19.1.0))(react@19.1.0) + '@react-native/assets-registry': 0.86.0 + '@react-native/codegen': 0.86.0(@babel/core@7.29.0) + '@react-native/community-cli-plugin': 0.86.0(@react-native/metro-config@0.86.0(@babel/core@7.29.0)) + '@react-native/gradle-plugin': 0.86.0 + '@react-native/js-polyfills': 0.86.0 + '@react-native/normalize-colors': 0.86.0 + '@react-native/virtualized-lists': 0.86.0(@types/react@19.2.17)(react-native@0.86.0(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.2.17)(react@19.2.6))(react@19.2.6) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.29.0) - babel-plugin-syntax-hermes-parser: 0.29.1 + babel-plugin-syntax-hermes-parser: 0.36.0 base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - glob: 7.2.3 + hermes-compiler: 250829098.0.14 invariant: 2.2.4 - jest-environment-node: 29.7.0 memoize-one: 5.2.1 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 + metro-runtime: 0.84.4 + metro-source-map: 0.84.4 nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 - react: 19.1.0 + react: 19.2.6 react-devtools-core: 6.1.5 react-refresh: 0.14.2 regenerator-runtime: 0.13.11 - scheduler: 0.26.0 + scheduler: 0.27.0 semver: 7.8.4 stacktrace-parser: 0.1.11 + tinyglobby: 0.2.15 whatwg-fetch: 3.6.20 - ws: 6.2.3 + ws: 7.5.10 yargs: 17.7.2 optionalDependencies: '@types/react': 19.2.17 @@ -29617,94 +29201,102 @@ snapshots: - supports-color - utf-8-validate + react-reconciler@0.33.0(react@19.2.6): + dependencies: + react: 19.2.6 + scheduler: 0.27.0 + react-refresh@0.14.2: {} react-refresh@0.17.0: {} react-refresh@0.18.0: {} - react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.1.0): + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.6): dependencies: - react: 19.1.0 - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.6) tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.1.0): + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.6): dependencies: - react: 19.1.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.1.0) - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.1.0) + react: 19.2.6 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.6) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.6) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.1.0) - use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.1.0) + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.6) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.6) optionalDependencies: '@types/react': 19.2.17 - react-resizable-panels@3.0.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-resizable-panels@3.0.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - react-resizable-panels@4.10.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-resizable-panels@4.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - react-scan@0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rollup@4.57.1): + react-scan@0.5.7(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(esbuild@0.27.2)(eslint@10.5.0(jiti@2.7.0))(react-devtools-core@6.1.5)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(rollup@4.57.1): dependencies: '@babel/core': 7.29.0 '@babel/types': 7.29.7 '@preact/signals': 2.9.2(preact@10.29.2) '@rollup/pluginutils': 5.3.0(rollup@4.57.1) - bippy: 0.5.42(react@19.1.0) + bippy: 0.5.42(react@19.2.6) commander: 14.0.3 picocolors: 1.1.1 preact: 10.29.2 prompts: 2.4.2 - react: 19.1.0 - react-doctor: 0.8.3(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(eslint@10.5.0(jiti@2.7.0)) - react-dom: 19.1.0(react@19.1.0) - react-grab: 0.1.48(react@19.1.0) + react: 19.2.6 + react-doctor: 0.9.2(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/exporter-trace-otlp-http@0.208.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(eslint@10.5.0(jiti@2.7.0))(react-devtools-core@6.1.5) + react-dom: 19.2.6(react@19.2.6) + react-grab: 0.1.50(react@19.2.6) optionalDependencies: esbuild: 0.27.2 unplugin: 3.0.0 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' + - '@types/react' + - bufferutil - eslint - oxlint-tsgolint + - react-devtools-core - rollup - supports-color + - utf-8-validate + - vite-plus - react-shadow@20.6.0(prop-types@15.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-shadow@20.6.0(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: humps: 2.0.1 prop-types: 15.8.1 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) - react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.1.0): + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.6): dependencies: get-nonce: 1.0.1 - react: 19.1.0 + react: 19.2.6 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - react-test-renderer@19.1.0(react@19.1.0): + react-test-renderer@19.2.6(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 react-is: 19.2.6 - scheduler: 0.26.0 + scheduler: 0.27.0 - react-zoom-pan-pinch@4.0.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-zoom-pan-pinch@4.0.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - - react@19.1.0: {} + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) react@19.2.6: {} @@ -29852,12 +29444,6 @@ snapshots: require-main-filename@2.0.0: {} - requireg@0.2.2: - dependencies: - nested-error-stacks: 2.0.1 - rc: 1.2.8 - resolve: 1.7.1 - requires-port@1.0.0: {} resedit@1.7.2: @@ -29881,26 +29467,16 @@ snapshots: resolve-from@5.0.0: {} - resolve-global@1.0.0: - dependencies: - global-dirs: 0.1.1 - resolve-pkg-maps@1.0.0: {} resolve-workspace-root@2.0.1: {} - resolve.exports@2.0.3: {} - resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@1.7.1: - dependencies: - path-parse: 1.0.7 - responselike@2.0.1: dependencies: lowercase-keys: 2.0.0 @@ -29910,6 +29486,11 @@ snapshots: onetime: 2.0.1 signal-exit: 3.0.7 + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -30094,12 +29675,6 @@ snapshots: safe-buffer@5.2.1: {} - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safer-buffer@2.1.2: {} sanitize-filename@1.6.4: @@ -30112,8 +29687,6 @@ snapshots: dependencies: xmlchars: 2.2.0 - scheduler@0.26.0: {} - scheduler@0.27.0: {} schema-utils@4.3.3: @@ -30135,10 +29708,6 @@ snapshots: semver@6.3.1: {} - semver@7.6.3: {} - - semver@7.7.2: {} - semver@7.7.3: {} semver@7.8.0: {} @@ -30219,15 +29788,6 @@ snapshots: set-blocking@2.0.0: {} - 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.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - setimmediate@1.0.5: {} setprototypeof@1.2.0: {} @@ -30285,8 +29845,6 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.3: {} - shell-quote@1.9.0: {} shiki@3.23.0: @@ -30390,6 +29948,11 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 + slice-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + slugify@1.6.6: {} smol-toml@1.6.0: {} @@ -30467,6 +30030,8 @@ snapshots: dependencies: type-fest: 0.7.1 + standard-navigation@0.0.5: {} + standardwebhooks@1.0.0: dependencies: '@stablelib/base64': 1.0.1 @@ -30484,10 +30049,10 @@ snapshots: stdin-discarder@0.3.2: {} - storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + storybook@10.4.1(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: '@storybook/global': 5.0.0 - '@storybook/icons': 2.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@storybook/icons': 2.0.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 @@ -30499,7 +30064,7 @@ snapshots: oxc-resolver: 11.20.0 recast: 0.23.11 semver: 7.8.4 - use-sync-external-store: 1.6.0(react@19.1.0) + use-sync-external-store: 1.6.0(react@19.2.6) ws: 8.19.0 optionalDependencies: '@types/react': 19.2.17 @@ -30760,8 +30325,6 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - temp-dir@2.0.0: {} - temp-file@3.4.0: dependencies: async-exit-hook: 2.0.1 @@ -30777,6 +30340,8 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 + terminal-size@4.0.1: {} + terser-webpack-plugin@5.3.16(@swc/core@1.15.43)(esbuild@0.27.2)(webpack@5.105.0(@swc/core@1.15.43)(esbuild@0.27.2)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -30805,7 +30370,7 @@ snapshots: terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 + acorn: 8.17.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -30851,8 +30416,8 @@ snapshots: tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinypool@2.1.0: {} @@ -30898,6 +30463,8 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 + toqr@0.1.1: {} + totalist@3.0.1: {} tough-cookie@5.1.2: @@ -31111,7 +30678,7 @@ snapshots: typebox@1.1.38: {} - typed-openapi@2.2.7(openapi-types@12.1.3)(react@19.1.0): + typed-openapi@2.2.7(openapi-types@12.1.3)(react@19.2.6): dependencies: '@apidevtools/swagger-parser': 12.1.0(openapi-types@12.1.3) '@sinclair/typebox-codegen': 0.11.1 @@ -31119,7 +30686,7 @@ snapshots: cac: 7.0.0 openapi3-ts: 4.5.0 oxfmt: 0.45.0 - pastable: 2.2.1(react@19.1.0) + pastable: 2.2.1(react@19.2.6) pathe: 2.0.3 ts-pattern: 5.9.0 transitivePeerDependencies: @@ -31150,8 +30717,6 @@ snapshots: undici-types@7.16.0: {} - undici@6.23.0: {} - undici@7.27.2: optional: true @@ -31186,10 +30751,6 @@ snapshots: dependencies: qs: 6.15.0 - unique-string@2.0.0: - dependencies: - crypto-random-string: 2.0.0 - unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -31223,7 +30784,7 @@ snapshots: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.15.0 - picomatch: 4.0.3 + picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 unplugin@3.0.0: @@ -31281,29 +30842,25 @@ snapshots: url-join@4.0.1: {} - use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.1.0): + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - use-latest-callback@0.2.6(react@19.1.0): + use-latest-callback@0.2.6(react@19.2.6): dependencies: - react: 19.1.0 + react: 19.2.6 - use-sidecar@1.1.3(@types/react@19.2.17)(react@19.1.0): + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.6): dependencies: detect-node-es: 1.1.0 - react: 19.1.0 + react: 19.2.6 tslib: 2.8.1 optionalDependencies: '@types/react': 19.2.17 - use-sync-external-store@1.6.0(react@19.1.0): - dependencies: - react: 19.1.0 - use-sync-external-store@1.6.0(react@19.2.6): dependencies: react: 19.2.6 @@ -31316,14 +30873,6 @@ snapshots: util-deprecate@1.0.2: {} - util@0.12.5: - dependencies: - inherits: 2.0.4 - is-arguments: 1.2.0 - is-generator-function: 1.1.2 - is-typed-array: 1.1.15 - which-typed-array: 1.1.20 - utils-merge@1.0.1: {} uuid@12.0.0: {} @@ -31346,11 +30895,11 @@ snapshots: vary@1.1.2: {} - vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6): dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -31370,10 +30919,10 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - virtua@0.48.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(solid-js@1.9.13): + virtua@0.48.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(solid-js@1.9.13): optionalDependencies: - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) solid-js: 1.9.13 vite-tsconfig-paths@6.1.1(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0))(typescript@5.9.3): @@ -31389,8 +30938,8 @@ snapshots: vite@7.3.5(@types/node@20.19.41)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.15 rollup: 4.57.1 tinyglobby: 0.2.15 @@ -31406,8 +30955,8 @@ snapshots: vite@7.3.5(@types/node@22.20.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.15 rollup: 4.57.1 tinyglobby: 0.2.15 @@ -31423,8 +30972,8 @@ snapshots: vite@7.3.5(@types/node@25.2.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.15 rollup: 4.57.1 tinyglobby: 0.2.15 @@ -31440,8 +30989,8 @@ snapshots: vite@7.3.5(@types/node@25.2.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.15 rollup: 4.57.1 tinyglobby: 0.2.15 @@ -31574,36 +31123,6 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@24.12.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(msw@2.12.8(@types/node@24.12.0)(typescript@5.9.3))(rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: rolldown-vite@7.3.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@types/node@24.12.0)(esbuild@0.27.2)(jiti@2.7.0)(terser@5.46.0)(tsx@4.22.4)(yaml@2.9.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 24.12.0 - '@vitest/ui': 4.1.8(vitest@4.1.8) - jsdom: 26.1.0 - transitivePeerDependencies: - - msw - vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@25.2.0)(@vitest/ui@4.1.8)(jsdom@26.1.0)(msw@2.12.8(@types/node@25.2.0)(typescript@5.9.3))(vite@7.3.5(@types/node@25.2.0)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 @@ -31747,8 +31266,6 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@5.0.0: {} - webidl-conversions@7.0.0: {} webpack-sources@3.3.3: @@ -31834,11 +31351,7 @@ snapshots: whatwg-mimetype@4.0.0: {} - whatwg-url-without-unicode@8.0.0-3: - dependencies: - buffer: 5.7.1 - punycode: 2.3.1 - webidl-conversions: 5.0.0 + whatwg-url-minimum@0.1.2: {} whatwg-url@14.2.0: dependencies: @@ -31854,16 +31367,6 @@ snapshots: which-module@2.0.1: {} - which-typed-array@1.1.20: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.9 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@1.3.1: dependencies: isexe: 2.0.0 @@ -31889,10 +31392,18 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - wonka@6.3.5: {} + widest-line@6.0.0: + dependencies: + string-width: 8.2.1 word-wrap@1.2.5: {} + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.1.2 + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -31926,24 +31437,17 @@ snapshots: signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@6.2.3: - dependencies: - async-limiter: 1.0.1 - ws@7.5.10: {} ws@8.19.0: {} + ws@8.21.1: {} + wsl-utils@0.1.0: dependencies: is-wsl: 3.1.0 @@ -32033,6 +31537,8 @@ snapshots: yoctocolors@2.1.2: {} + yoga-layout@3.2.1: {} + yoga-wasm-web@0.3.3: {} zod-to-json-schema@3.25.1(zod@3.25.76): @@ -32051,14 +31557,6 @@ snapshots: zod@4.4.3: {} - zustand@4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.1.0): - dependencies: - use-sync-external-store: 1.6.0(react@19.1.0) - optionalDependencies: - '@types/react': 19.2.17 - immer: 11.1.3 - react: 19.1.0 - zustand@4.5.7(@types/react@19.2.17)(immer@11.1.3)(react@19.2.6): dependencies: use-sync-external-store: 1.6.0(react@19.2.6) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8efd33591d..fea79dbf08 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -27,8 +27,8 @@ catalog: '@types/react-dom': ^19.2.3 hono: ^4.6.14 inversify: ^7.10.6 - react: 19.1.0 - react-dom: 19.1.0 + react: 19.2.6 + react-dom: 19.2.6 reflect-metadata: ^0.2.2 superjson: ^2.2.2 tsup: ^8.5.1 @@ -83,11 +83,10 @@ overrides: # Dedupe the zod 4.x line to one version so cross-package schema types stay # nameable (TS2742). Scoped to 4.x; 3.x consumers are untouched. 'zod@^4.0.0': 4.4.3 - # Keep one React across the monorepo so shared packages resolve one runtime; - # Expo 54 and React Native 0.81 embed the React 19.1 renderer. - react: 19.1.0 - react-dom: 19.1.0 - react-test-renderer: 19.1.0 + # Keep one React across the monorepo so shared packages resolve one runtime. + react: 19.2.6 + react-dom: 19.2.6 + react-test-renderer: 19.2.6 # Dedupe @types/react so shared UI Ref types unify across apps (a second copy # makes nominally-distinct Ref types and breaks ref props in packages/ui). '@types/react': ^19.2.15 From 5e0b2931f17856590520dd8fb3c8f9bbee4d4a4e Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:38:12 +0300 Subject: [PATCH 34/42] feat(mobile): add Codex task controls Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 74 ++++++++++++++++-- apps/mobile/src/app/task/index.tsx | 77 +++++++++++++++---- .../src/features/tasks/composer/Pill.tsx | 15 +++- .../tasks/composer/TaskChatComposer.tsx | 64 ++++++++++++--- .../src/features/tasks/stores/taskStore.ts | 7 +- .../core/src/sessions/executionModes.test.ts | 13 ++++ packages/core/src/sessions/executionModes.ts | 7 ++ 7 files changed, 222 insertions(+), 35 deletions(-) create mode 100644 packages/core/src/sessions/executionModes.test.ts diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index 31dc3d6e5d..a0a4a7ba12 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -6,10 +6,13 @@ import { } from "@posthog/core/sessions/sessionActivity"; import { isTaskRunning } from "@posthog/core/tasks/taskArchive"; import { + type Adapter, + DEFAULT_CODEX_MODEL, DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, getReasoningEffortOptions, + isSupportedReasoningEffort, type SupportedReasoningEffort, serializeCloudPrompt, type Task, @@ -167,11 +170,36 @@ export default function TaskDetailScreen() { const [initialComposerMessage, setInitialComposerMessage] = useState< string | undefined >(); + const composerAdapter: Adapter = + task?.latest_run?.runtime_adapter && + composerConfig?.adapter !== task.latest_run.runtime_adapter + ? task.latest_run.runtime_adapter + : (composerConfig?.adapter ?? + task?.latest_run?.runtime_adapter ?? + "claude"); + const composerConfigMatchesAdapter = + composerConfig?.adapter === undefined + ? composerAdapter === "claude" + : composerConfig.adapter === composerAdapter; const composerMode: ExecutionMode = - composerConfig?.mode ?? DEFAULT_CLAUDE_EXECUTION_MODE; - const composerModel = composerConfig?.model ?? DEFAULT_GATEWAY_MODEL; + (composerConfigMatchesAdapter ? composerConfig?.mode : undefined) ?? + DEFAULT_CLAUDE_EXECUTION_MODE; + const composerModel = + (composerConfigMatchesAdapter ? composerConfig?.model : undefined) ?? + task?.latest_run?.model ?? + (composerAdapter === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL); + const requestedComposerReasoning = composerConfigMatchesAdapter + ? composerConfig?.reasoning + : undefined; const composerReasoning: SupportedReasoningEffort = - composerConfig?.reasoning ?? DEFAULT_REASONING_EFFORT; + requestedComposerReasoning && + isSupportedReasoningEffort( + composerAdapter, + composerModel, + requestedComposerReasoning, + ) + ? requestedComposerReasoning + : DEFAULT_REASONING_EFFORT; const messagingMode = useMessagingMode(taskId); const queuedCount = useQueuedCount(taskId); @@ -315,14 +343,14 @@ export default function TaskDetailScreen() { : text; const supportsReasoning = - getReasoningEffortOptions("claude", composerModel) !== null; + getReasoningEffortOptions(composerAdapter, composerModel) !== null; const updatedTask = await getPostHogApiClient().runTaskInCloud( taskId, undefined, { resumeFromRunId: task.latest_run?.id, pendingUserMessage, - adapter: "claude", + adapter: composerAdapter, model: composerModel, reasoningLevel: supportsReasoning ? composerReasoning : undefined, initialPermissionMode: composerMode, @@ -351,6 +379,7 @@ export default function TaskDetailScreen() { connectToTask, updateTaskInCache, composerMode, + composerAdapter, composerModel, composerReasoning, ], @@ -507,6 +536,19 @@ export default function TaskDetailScreen() { [taskId, setComposerConfig, setConfigOption], ); + const handleAdapterChange = useCallback( + (value: Adapter) => { + if (!taskId) return; + setComposerConfig(taskId, { + adapter: value, + mode: DEFAULT_CLAUDE_EXECUTION_MODE, + model: value === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL, + reasoning: DEFAULT_REASONING_EFFORT, + }); + }, + [taskId, setComposerConfig], + ); + const handleModelChange = useCallback( (value: string) => { if (!taskId) return; @@ -575,6 +617,13 @@ export default function TaskDetailScreen() { undefined, { resumeFromRunId: task.latest_run?.id, + adapter: composerAdapter, + model: composerModel, + reasoningLevel: + getReasoningEffortOptions(composerAdapter, composerModel) !== null + ? composerReasoning + : undefined, + initialPermissionMode: composerMode, rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, }, ); @@ -591,7 +640,17 @@ export default function TaskDetailScreen() { "Could not restart the task. Please try again.", ); } - }, [taskId, task, disconnectFromTask, connectToTask, updateTaskInCache]); + }, [ + taskId, + task, + disconnectFromTask, + connectToTask, + updateTaskInCache, + composerAdapter, + composerModel, + composerReasoning, + composerMode, + ]); // Clear retrying once the agent finishes a turn or the run terminates. useEffect(() => { @@ -785,6 +844,8 @@ export default function TaskDetailScreen() { /> ) : null} ("claude"); const { configOptions, hasLiveConfig, isConfigReady } = - useCloudTaskConfigOptions("claude"); + useCloudTaskConfigOptions(adapter); + const executionModes = getMobileExecutionModes( + getAvailableModesForAdapter(adapter), + ); const modelConfigOption = getModelConfigOption(configOptions); const mobileModelOptions = getComposerModelOptions(modelConfigOption); const { @@ -197,7 +201,9 @@ export default function NewTaskScreen() { const prefs = usePreferencesStore.getState(); if (prefs.defaultInitialTaskMode === "last_used") { const last = prefs.lastNewTaskMode; - const isValidMode = EXECUTION_MODES.some((mode) => mode.id === last); + const isValidMode = getMobileExecutionModes( + getAvailableModesForAdapter("claude"), + ).some((mode) => mode.id === last); if (isValidMode) return last as ExecutionMode; } return DEFAULT_CLAUDE_EXECUTION_MODE; @@ -217,16 +223,17 @@ export default function NewTaskScreen() { useEffect(() => { if (!hasLiveConfig) return; const next = resolveCloudComposerModelChange({ - adapter: "claude", + adapter, modelOption: modelConfigOption, requestedModel: model, reasoning, }); if (next.model !== model) setModel(next.model); if (next.reasoning !== reasoning) setReasoning(next.reasoning); - }, [hasLiveConfig, model, modelConfigOption, reasoning]); + }, [adapter, hasLiveConfig, model, modelConfigOption, reasoning]); const [creating, setCreating] = useState(false); const [repoSheetOpen, setRepoSheetOpen] = useState(false); + const [adapterSheetOpen, setAdapterSheetOpen] = useState(false); const [modeSheetOpen, setModeSheetOpen] = useState(false); const [modelSheetOpen, setModelSheetOpen] = useState(false); const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); @@ -363,7 +370,7 @@ export default function NewTaskScreen() { // user picked here, so the task detail screen reflects them and every // subsequent run (resume-after-terminal) reuses the selected mode rather // than falling back to the default plan mode. - setComposerConfig(task.id, { mode, model, reasoning }); + setComposerConfig(task.id, { adapter, mode, model, reasoning }); const pendingUserMessage = attachments.length > 0 @@ -373,11 +380,11 @@ export default function NewTaskScreen() { : trimmedPrompt; const supportsReasoning = - getReasoningEffortOptions("claude", model) !== null; + getReasoningEffortOptions(adapter, model) !== null; await client.runTaskInCloud(task.id, undefined, { pendingUserMessage, - adapter: "claude", + adapter, model, reasoningLevel: supportsReasoning ? reasoning : undefined, initialPermissionMode: mode, @@ -401,6 +408,7 @@ export default function NewTaskScreen() { } }, [ attachments, + adapter, creating, mode, model, @@ -419,7 +427,7 @@ export default function NewTaskScreen() { hasContent && isRepositorySelectionComplete(selection) && !creating; - const reasoningOptions = getReasoningEffortOptions("claude", model) ?? []; + const reasoningOptions = getReasoningEffortOptions(adapter, model) ?? []; const showReasoningPill = reasoningOptions.length > 0; // Best-effort prewarm; failures are swallowed. `selection.integrationId` is @@ -429,7 +437,7 @@ export default function NewTaskScreen() { repository: selection.repository, githubIntegrationId: selection.integrationId, composerIsEmpty: !hasContent || !isConfigReady, - runtimeAdapter: "claude", + runtimeAdapter: adapter, model, reasoningEffort: showReasoningPill ? reasoning : null, }); @@ -636,6 +644,13 @@ export default function NewTaskScreen() { paddingRight: 16, }} > + } + label={adapter === "codex" ? "Codex" : "Claude"} + accent={adapter === "codex"} + onPress={() => setAdapterSheetOpen(true)} + /> + option.id === mode) + executionModes.find((option) => option.id === mode) ?.name ?? mode } accent={mode === "plan"} @@ -749,6 +764,38 @@ export default function NewTaskScreen() { + { + const nextAdapter = value as Adapter; + setAdapter(nextAdapter); + setMode(DEFAULT_CLAUDE_EXECUTION_MODE); + setModel( + nextAdapter === "codex" + ? DEFAULT_CODEX_MODEL + : DEFAULT_GATEWAY_MODEL, + ); + setReasoning(DEFAULT_REASONING_EFFORT); + }} + onClose={() => setAdapterSheetOpen(false)} + options={[ + { + value: "claude", + label: "Claude Code", + description: "Anthropic's coding agent", + icon: , + }, + { + value: "codex", + label: "Codex", + description: "OpenAI's coding agent", + icon: , + }, + ]} + /> + setModeSheetOpen(false)} - options={EXECUTION_MODES.map((executionMode) => ({ + options={executionModes.map((executionMode) => ({ value: executionMode.id, label: executionMode.name, description: executionMode.description, @@ -779,7 +826,7 @@ export default function NewTaskScreen() { value={model} onChange={(value) => { const next = resolveCloudComposerModelChange({ - adapter: "claude", + adapter, modelOption: modelConfigOption, requestedModel: value, reasoning, diff --git a/apps/mobile/src/features/tasks/composer/Pill.tsx b/apps/mobile/src/features/tasks/composer/Pill.tsx index 5860c05d0c..94446f3770 100644 --- a/apps/mobile/src/features/tasks/composer/Pill.tsx +++ b/apps/mobile/src/features/tasks/composer/Pill.tsx @@ -11,14 +11,23 @@ interface PillProps { placeholder?: boolean; /** Tone the label in accent (used for Plan Mode in the desktop). */ accent?: boolean; - onPress: () => void; + onPress?: () => void; + disabled?: boolean; } -export function Pill({ icon, label, placeholder, accent, onPress }: PillProps) { +export function Pill({ + icon, + label, + placeholder, + accent, + onPress, + disabled = false, +}: PillProps) { const themeColors = useThemeColors(); return ( {icon ? {icon} : null} @@ -34,7 +43,7 @@ export function Pill({ icon, label, placeholder, accent, onPress }: PillProps) { > {label} - + {disabled ? null : } ); } diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index b308f7ebc8..d3c0ebc7d9 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -1,10 +1,11 @@ import { Text } from "@components/text"; import { DEFAULT_CLAUDE_EXECUTION_MODE, - getAvailableModes, + getAvailableModesForAdapter, } from "@posthog/core/sessions/executionModes"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { + type Adapter, DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, @@ -72,8 +73,6 @@ import { } from "./submitComposerMessage"; const log = logger.scope("task-chat-composer"); -const EXECUTION_MODES = getMobileExecutionModes(getAvailableModes()); - interface TaskChatComposerProps { onSend: ( message: string, @@ -85,9 +84,12 @@ interface TaskChatComposerProps { initialMessage?: string; isUserTurn?: boolean; /** Current pill values (persisted per-task by the caller). */ + adapter: Adapter; mode: ExecutionMode; model: string; reasoning: SupportedReasoningEffort; + onAdapterChange: (adapter: Adapter) => void; + canChangeAdapter?: boolean; onModeChange: (mode: ExecutionMode) => void; onModelChange: (model: string) => void; onReasoningChange: (reasoning: SupportedReasoningEffort) => void; @@ -181,9 +183,12 @@ export function TaskChatComposer({ placeholder = "Ask a question", initialMessage, isUserTurn = false, + adapter, mode, model, reasoning, + onAdapterChange, + canChangeAdapter = true, onModeChange, onModelChange, onReasoningChange, @@ -195,7 +200,10 @@ export function TaskChatComposer({ onCancelEdit, }: TaskChatComposerProps) { const themeColors = useThemeColors(); - const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions("claude"); + const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions(adapter); + const executionModes = getMobileExecutionModes( + getAvailableModesForAdapter(adapter), + ); const modelConfigOption = getModelConfigOption(configOptions); const mobileModelOptions = getComposerModelOptions(modelConfigOption); const [message, setMessage] = useState(() => initialMessage ?? ""); @@ -224,7 +232,7 @@ export function TaskChatComposer({ useEffect(() => { if (!hasLiveConfig) return; const next = resolveCloudComposerModelChange({ - adapter: "claude", + adapter, modelOption: modelConfigOption, requestedModel: model, reasoning, @@ -232,6 +240,7 @@ export function TaskChatComposer({ if (next.model !== model) onModelChange(next.model); if (next.reasoning !== reasoning) onReasoningChange(next.reasoning); }, [ + adapter, hasLiveConfig, model, modelConfigOption, @@ -251,10 +260,11 @@ export function TaskChatComposer({ const isTranscribing = status === "transcribing"; const [modeSheetOpen, setModeSheetOpen] = useState(false); + const [adapterSheetOpen, setAdapterSheetOpen] = useState(false); const [modelSheetOpen, setModelSheetOpen] = useState(false); const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); - const reasoningOptions = getReasoningEffortOptions("claude", model) ?? []; + const reasoningOptions = getReasoningEffortOptions(adapter, model) ?? []; const showReasoningPill = reasoningOptions.length > 0; const hasContent = !isComposerEmpty({ text: message, attachments }); @@ -417,6 +427,18 @@ export function TaskChatComposer({ paddingRight: 4, }} > + } + label={adapter === "codex" ? "Codex" : "Claude"} + accent={adapter === "codex"} + onPress={ + canChangeAdapter + ? () => setAdapterSheetOpen(true) + : undefined + } + disabled={!canChangeAdapter} + /> + option.id === mode) - ?.name ?? mode + executionModes.find((option) => option.id === mode)?.name ?? + mode } accent={mode === "plan"} onPress={() => setModeSheetOpen(true)} @@ -507,13 +529,35 @@ export function TaskChatComposer({ + onAdapterChange(value as Adapter)} + onClose={() => setAdapterSheetOpen(false)} + options={[ + { + value: "claude", + label: "Claude Code", + description: "Anthropic's coding agent", + icon: , + }, + { + value: "codex", + label: "Codex", + description: "OpenAI's coding agent", + icon: , + }, + ]} + /> + onModeChange(v as ExecutionMode)} onClose={() => setModeSheetOpen(false)} - options={EXECUTION_MODES.map((m) => ({ + options={executionModes.map((m) => ({ value: m.id, label: m.name, description: m.description, @@ -531,7 +575,7 @@ export function TaskChatComposer({ value={model} onChange={(v) => { const next = resolveCloudComposerModelChange({ - adapter: "claude", + adapter, modelOption: modelConfigOption, requestedModel: v, reasoning, diff --git a/apps/mobile/src/features/tasks/stores/taskStore.ts b/apps/mobile/src/features/tasks/stores/taskStore.ts index 39276a7eeb..f7589e0333 100644 --- a/apps/mobile/src/features/tasks/stores/taskStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskStore.ts @@ -1,5 +1,9 @@ import type { TaskActivitySortMode } from "@posthog/core/tasks/taskActivity"; -import type { ExecutionMode, SupportedReasoningEffort } from "@posthog/shared"; +import type { + Adapter, + ExecutionMode, + SupportedReasoningEffort, +} from "@posthog/shared"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; @@ -16,6 +20,7 @@ const EMPTY_REPOSITORY_SELECTION: RepositorySelection = { /** Per-task chat composer pill values. Persisted so reopening a task keeps * the mode/model/reasoning the user last selected for it. */ export interface TaskComposerConfig { + adapter?: Adapter; mode?: ExecutionMode; model?: string; reasoning?: SupportedReasoningEffort; diff --git a/packages/core/src/sessions/executionModes.test.ts b/packages/core/src/sessions/executionModes.test.ts new file mode 100644 index 0000000000..88bbfe2b64 --- /dev/null +++ b/packages/core/src/sessions/executionModes.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from "vitest"; +import { getAvailableModesForAdapter } from "./executionModes"; + +describe("getAvailableModesForAdapter", () => { + it.each([ + ["claude", ["default", "acceptEdits", "plan", "bypassPermissions", "auto"]], + ["codex", ["plan", "read-only", "auto", "full-access"]], + ] as const)("returns %s execution modes", (adapter, expected) => { + expect(getAvailableModesForAdapter(adapter).map((mode) => mode.id)).toEqual( + expected, + ); + }); +}); diff --git a/packages/core/src/sessions/executionModes.ts b/packages/core/src/sessions/executionModes.ts index 2ccbadf354..8d4f7e920f 100644 --- a/packages/core/src/sessions/executionModes.ts +++ b/packages/core/src/sessions/executionModes.ts @@ -7,6 +7,7 @@ export interface ModeInfo { } export const DEFAULT_CLAUDE_EXECUTION_MODE: ExecutionMode = "plan"; +export const DEFAULT_CODEX_EXECUTION_MODE: ExecutionMode = "plan"; const availableModes: ModeInfo[] = [ { @@ -46,3 +47,9 @@ export function getAvailableModes(): ModeInfo[] { export function getAvailableCodexModes(): ModeInfo[] { return [...CODEX_MODE_PRESETS]; } + +export function getAvailableModesForAdapter( + adapter: "claude" | "codex", +): ModeInfo[] { + return adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(); +} From f954c026111c0d70865850751206a6a4859557d0 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:38:38 +0300 Subject: [PATCH 35/42] feat(mobile): align task composer with desktop Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/index.tsx | 529 +++++++++--------- .../tasks/composer/TaskChatComposer.tsx | 86 +-- 2 files changed, 308 insertions(+), 307 deletions(-) diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index a24b14c235..a69fa24aa4 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -21,6 +21,7 @@ import { ArrowUp, BrainIcon, CaretDown, + Cpu, GithubLogo, MicrophoneIcon, PaperclipIcon, @@ -91,6 +92,7 @@ import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { toRgba, useThemeColors } from "@/lib/theme"; const log = logger.scope("task-create"); +const SWITCH_ADAPTER_VALUE = "__switch_adapter__"; const SUGGESTIONS = [ "Create or update my CLAUDE.md file", "Search for a TODO comment and fix it", @@ -233,7 +235,6 @@ export default function NewTaskScreen() { }, [adapter, hasLiveConfig, model, modelConfigOption, reasoning]); const [creating, setCreating] = useState(false); const [repoSheetOpen, setRepoSheetOpen] = useState(false); - const [adapterSheetOpen, setAdapterSheetOpen] = useState(false); const [modeSheetOpen, setModeSheetOpen] = useState(false); const [modelSheetOpen, setModelSheetOpen] = useState(false); const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); @@ -508,294 +509,267 @@ export default function NewTaskScreen() { - - {repoSheetOpen ? null : prompt.trim().length === 0 ? ( - - - Suggestions - - - {SUGGESTIONS.map((suggestion) => ( - setPrompt(suggestion)} - className="rounded-2xl border border-gray-6 bg-card px-4 py-3 active:bg-gray-2" - > - - {suggestion} - - - ))} - - - ) : null} - - {/* Inline repo picker: pops up directly above the pill when - open, replacing the suggestions area. Rendered inline (not - a Modal) so it feels like a dropdown anchored to the pill - rather than a slide-in sheet. */} - - - setSelection(toRepositorySelection(option)) - } - onClose={() => setRepoSheetOpen(false)} - /> - - - - - {repositoryWarning ? ( - - ) : null} - - - setRepoSheetOpen((prev) => !prev)} - className={`flex-row items-center gap-2 rounded-full border py-1.5 pr-2.5 pl-2 active:bg-gray-2 ${ - repoSheetOpen - ? "border-accent-7 bg-accent-3" - : "border-gray-6 bg-card" - }`} - > - + + + + setSelection(toRepositorySelection(option)) } - weight={selectedRepositoryOption ? "fill" : "regular"} + onClose={() => setRepoSheetOpen(false)} /> - - {repositoryLabel} - - + + {repositoryWarning ? ( + - - + ) : null} - - - - - + setAttachmentSheetOpen(true)} - accessibilityLabel="Add attachment" - accessibilityRole="button" - className="h-9 w-9 items-center justify-center active:opacity-60" + onPress={() => setRepoSheetOpen((prev) => !prev)} + className={`flex-row items-center gap-2 rounded-md border py-1.5 pr-2.5 pl-2 active:bg-gray-2 ${ + repoSheetOpen + ? "border-accent-7 bg-accent-3" + : "border-gray-6 bg-card" + }`} > - 0 - ? themeColors.accent[11] + selectedRepositoryOption + ? themeColors.gray[12] : themeColors.gray[10] } - weight={attachments.length > 0 ? "fill" : "regular"} + weight={selectedRepositoryOption ? "fill" : "regular"} + /> + + {repositoryLabel} + + + - - - } - label={adapter === "codex" ? "Codex" : "Claude"} - accent={adapter === "codex"} - onPress={() => setAdapterSheetOpen(true)} - /> + + + - + setAttachmentSheetOpen(true)} + accessibilityLabel="Add attachment" + accessibilityRole="button" + className="h-9 w-9 items-center justify-center active:opacity-60" + > + 0 ? themeColors.accent[11] - : themeColors.gray[11], - )} - label={ - executionModes.find((option) => option.id === mode) - ?.name ?? mode - } - accent={mode === "plan"} - onPress={() => setModeSheetOpen(true)} - /> - - } - label={ - getConfigOptionLabel( - modelConfigOption.options, - model, - ) ?? model + : themeColors.gray[10] } - onPress={() => setModelSheetOpen(true)} + weight={attachments.length > 0 ? "fill" : "regular"} /> + + + + + option.id === mode) + ?.name ?? mode + } + accent={mode === "plan"} + onPress={() => setModeSheetOpen(true)} + /> - {showReasoningPill ? ( + adapter === "codex" ? ( + + ) : ( + + ) } label={ - reasoningOptions.find( - (option) => option.value === reasoning, - )?.name ?? reasoning + getConfigOptionLabel( + modelConfigOption.options, + model, + ) ?? model } - onPress={() => setReasoningSheetOpen(true)} + onPress={() => setModelSheetOpen(true)} /> - ) : null} - - {/* Right-edge fade hints that more pills exist when the row + + {showReasoningPill ? ( + + } + label={ + reasoningOptions.find( + (option) => option.value === reasoning, + )?.name ?? reasoning + } + onPress={() => setReasoningSheetOpen(true)} + /> + ) : null} + + {/* Right-edge fade hints that more pills exist when the row overflows. Non-interactive so taps fall through. */} - + + + + + {creating || isTranscribing ? ( + + ) : isRecording ? ( + + ) : hasContent ? ( + + ) : ( + + )} + + - - {creating || isTranscribing ? ( - - ) : isRecording ? ( - - ) : hasContent ? ( - - ) : ( - - )} - - + + Suggestions + + + {SUGGESTIONS.map((suggestion) => ( + setPrompt(suggestion)} + className="rounded-lg border border-gray-5 bg-gray-2 px-3 py-2.5 active:bg-gray-3" + > + + {suggestion} + + + ))} + + + ) : null} - { - const nextAdapter = value as Adapter; - setAdapter(nextAdapter); - setMode(DEFAULT_CLAUDE_EXECUTION_MODE); - setModel( - nextAdapter === "codex" - ? DEFAULT_CODEX_MODEL - : DEFAULT_GATEWAY_MODEL, - ); - setReasoning(DEFAULT_REASONING_EFFORT); - }} - onClose={() => setAdapterSheetOpen(false)} - options={[ - { - value: "claude", - label: "Claude Code", - description: "Anthropic's coding agent", - icon: , - }, - { - value: "codex", - label: "Codex", - description: "OpenAI's coding agent", - icon: , - }, - ]} - /> - { + if (value === SWITCH_ADAPTER_VALUE) { + const nextAdapter: Adapter = + adapter === "claude" ? "codex" : "claude"; + setAdapter(nextAdapter); + setMode(DEFAULT_CLAUDE_EXECUTION_MODE); + setModel( + nextAdapter === "codex" + ? DEFAULT_CODEX_MODEL + : DEFAULT_GATEWAY_MODEL, + ); + setReasoning(DEFAULT_REASONING_EFFORT); + return; + } const next = resolveCloudComposerModelChange({ adapter, modelOption: modelConfigOption, @@ -835,13 +822,27 @@ export default function NewTaskScreen() { setReasoning(next.reasoning); }} onClose={() => setModelSheetOpen(false)} - options={mobileModelOptions.map((modelOption) => ({ - value: modelOption.value, - label: modelOption.label, - description: modelOption.description, - disabled: modelOption.disabled, - icon: , - }))} + options={[ + ...mobileModelOptions.map((modelOption) => ({ + value: modelOption.value, + label: modelOption.label, + description: modelOption.description, + disabled: modelOption.disabled, + icon: + adapter === "codex" ? ( + + ) : ( + + ), + })), + { + value: SWITCH_ADAPTER_VALUE, + label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`, + description: "Change coding agent", + disabled: false, + icon: , + }, + ]} /> - } - label={adapter === "codex" ? "Codex" : "Claude"} - accent={adapter === "codex"} - onPress={ - canChangeAdapter - ? () => setAdapterSheetOpen(true) - : undefined - } - disabled={!canChangeAdapter} - /> - } + icon={ + adapter === "codex" ? ( + + ) : ( + + ) + } label={ getConfigOptionLabel(modelConfigOption.options, model) ?? model @@ -529,28 +524,6 @@ export function TaskChatComposer({ - onAdapterChange(value as Adapter)} - onClose={() => setAdapterSheetOpen(false)} - options={[ - { - value: "claude", - label: "Claude Code", - description: "Anthropic's coding agent", - icon: , - }, - { - value: "codex", - label: "Codex", - description: "OpenAI's coding agent", - icon: , - }, - ]} - /> - { + if (v === SWITCH_ADAPTER_VALUE) { + onAdapterChange(adapter === "claude" ? "codex" : "claude"); + return; + } const next = resolveCloudComposerModelChange({ adapter, modelOption: modelConfigOption, @@ -586,13 +563,36 @@ export function TaskChatComposer({ } }} onClose={() => setModelSheetOpen(false)} - options={mobileModelOptions.map((m) => ({ - value: m.value, - label: m.label, - description: m.description, - disabled: m.disabled, - icon: , - }))} + options={[ + ...mobileModelOptions.map((m) => ({ + value: m.value, + label: m.label, + description: m.description, + disabled: m.disabled, + icon: + adapter === "codex" ? ( + + ) : ( + + ), + })), + ...(canChangeAdapter + ? [ + { + value: SWITCH_ADAPTER_VALUE, + label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`, + description: "Change coding agent", + disabled: false, + icon: + adapter === "claude" ? ( + + ) : ( + + ), + }, + ] + : []), + ]} /> Date: Fri, 24 Jul 2026 19:38:48 +0300 Subject: [PATCH 36/42] feat(mobile): align task detail with desktop Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 1 + .../tasks/components/FloatingTaskHeader.tsx | 34 +----- .../tasks/composer/TaskChatComposer.tsx | 107 ++++-------------- 3 files changed, 29 insertions(+), 113 deletions(-) diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index a0a4a7ba12..1ac887fa1d 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -844,6 +844,7 @@ export default function TaskDetailScreen() { /> ) : null} - - - + (null); - - useEffect(() => { - if (active) { - opacity.setValue(0); - animRef.current = Animated.loop( - Animated.sequence([ - Animated.timing(opacity, { - toValue: 1, - duration: 1500, - easing: Easing.inOut(Easing.ease), - useNativeDriver: true, - }), - Animated.timing(opacity, { - toValue: 0, - duration: 1500, - easing: Easing.inOut(Easing.ease), - useNativeDriver: true, - }), - ]), - ); - animRef.current.start(); - } else { - animRef.current?.stop(); - animRef.current = null; - opacity.setValue(0); - } - return () => { - animRef.current?.stop(); - }; - }, [active, opacity]); - - if (!active) return null; - - return ( - - ); -} - export function TaskChatComposer({ onSend, onStop, @@ -352,10 +290,9 @@ export function TaskChatComposer({ return ( <> - - - - + + + {editing ? ( @@ -381,7 +318,7 @@ export function TaskChatComposer({ /> - - ) : ( - - ) - } - label={messagingModeLabel} - accent={isSteer} - onPress={handleToggleMessagingMode} - /> - setReasoningSheetOpen(true)} /> ) : null} + + + ) : ( + + ) + } + label={messagingModeLabel} + accent={isSteer} + onPress={handleToggleMessagingMode} + /> Date: Fri, 24 Jul 2026 19:38:55 +0300 Subject: [PATCH 37/42] fix(mobile): reuse desktop adapter defaults Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 8 ++++++-- apps/mobile/src/app/task/index.tsx | 3 ++- .../features/tasks/composer/TaskChatComposer.tsx | 5 ++++- packages/core/src/sessions/cloudSessionConfig.ts | 11 ++++------- packages/core/src/sessions/executionModes.test.ts | 14 +++++++++++++- packages/core/src/sessions/executionModes.ts | 7 ++++++- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index 1ac887fa1d..a43104f3de 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -1,5 +1,8 @@ import { Text } from "@components/text"; -import { DEFAULT_CLAUDE_EXECUTION_MODE } from "@posthog/core/sessions/executionModes"; +import { + DEFAULT_CLAUDE_EXECUTION_MODE, + getDefaultExecutionModeForAdapter, +} from "@posthog/core/sessions/executionModes"; import { countUserMessages, getSessionActivityPhase, @@ -172,6 +175,7 @@ export default function TaskDetailScreen() { >(); const composerAdapter: Adapter = task?.latest_run?.runtime_adapter && + !session?.terminalStatus && composerConfig?.adapter !== task.latest_run.runtime_adapter ? task.latest_run.runtime_adapter : (composerConfig?.adapter ?? @@ -541,7 +545,7 @@ export default function TaskDetailScreen() { if (!taskId) return; setComposerConfig(taskId, { adapter: value, - mode: DEFAULT_CLAUDE_EXECUTION_MODE, + mode: getDefaultExecutionModeForAdapter(value), model: value === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL, reasoning: DEFAULT_REASONING_EFFORT, }); diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index a69fa24aa4..7257cf5240 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -2,6 +2,7 @@ import { Text } from "@components/text"; import { DEFAULT_CLAUDE_EXECUTION_MODE, getAvailableModesForAdapter, + getDefaultExecutionModeForAdapter, } from "@posthog/core/sessions/executionModes"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { @@ -803,7 +804,7 @@ export default function NewTaskScreen() { const nextAdapter: Adapter = adapter === "claude" ? "codex" : "claude"; setAdapter(nextAdapter); - setMode(DEFAULT_CLAUDE_EXECUTION_MODE); + setMode(getDefaultExecutionModeForAdapter(nextAdapter)); setModel( nextAdapter === "codex" ? DEFAULT_CODEX_MODEL diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index 3c6dddf971..25eda788fa 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -2,6 +2,7 @@ import { Text } from "@components/text"; import { DEFAULT_CLAUDE_EXECUTION_MODE, getAvailableModesForAdapter, + getDefaultExecutionModeForAdapter, } from "@posthog/core/sessions/executionModes"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { @@ -485,7 +486,9 @@ export function TaskChatComposer({ value={model} onChange={(v) => { if (v === SWITCH_ADAPTER_VALUE) { - onAdapterChange(adapter === "claude" ? "codex" : "claude"); + const nextAdapter = adapter === "claude" ? "codex" : "claude"; + onAdapterChange(nextAdapter); + onModeChange(getDefaultExecutionModeForAdapter(nextAdapter)); return; } const next = resolveCloudComposerModelChange({ diff --git a/packages/core/src/sessions/cloudSessionConfig.ts b/packages/core/src/sessions/cloudSessionConfig.ts index b6a9e4f8f4..1a7799b3dc 100644 --- a/packages/core/src/sessions/cloudSessionConfig.ts +++ b/packages/core/src/sessions/cloudSessionConfig.ts @@ -1,9 +1,8 @@ import type { SessionConfigOption } from "@agentclientprotocol/sdk"; import type { Adapter, StoredLogEntry } from "@posthog/shared"; import { - DEFAULT_CLAUDE_EXECUTION_MODE, - getAvailableCodexModes, - getAvailableModes, + getAvailableModesForAdapter, + getDefaultExecutionModeForAdapter, } from "./executionModes"; /** @@ -56,10 +55,8 @@ export function buildCloudDefaultConfigOptions( adapter: Adapter = "claude", extra: SessionConfigOption[] = [], ): SessionConfigOption[] { - const modes = - adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(); - const fallbackMode = - adapter === "codex" ? "auto" : DEFAULT_CLAUDE_EXECUTION_MODE; + const modes = getAvailableModesForAdapter(adapter); + const fallbackMode = getDefaultExecutionModeForAdapter(adapter); const currentMode = typeof initialMode === "string" && modes.some((mode) => mode.id === initialMode) diff --git a/packages/core/src/sessions/executionModes.test.ts b/packages/core/src/sessions/executionModes.test.ts index 88bbfe2b64..9246e9ce67 100644 --- a/packages/core/src/sessions/executionModes.test.ts +++ b/packages/core/src/sessions/executionModes.test.ts @@ -1,5 +1,8 @@ import { describe, expect, it } from "vitest"; -import { getAvailableModesForAdapter } from "./executionModes"; +import { + getAvailableModesForAdapter, + getDefaultExecutionModeForAdapter, +} from "./executionModes"; describe("getAvailableModesForAdapter", () => { it.each([ @@ -11,3 +14,12 @@ describe("getAvailableModesForAdapter", () => { ); }); }); + +describe("getDefaultExecutionModeForAdapter", () => { + it.each([ + ["claude", "plan"], + ["codex", "auto"], + ] as const)("returns the desktop default for %s", (adapter, expected) => { + expect(getDefaultExecutionModeForAdapter(adapter)).toBe(expected); + }); +}); diff --git a/packages/core/src/sessions/executionModes.ts b/packages/core/src/sessions/executionModes.ts index 8d4f7e920f..a415b2716c 100644 --- a/packages/core/src/sessions/executionModes.ts +++ b/packages/core/src/sessions/executionModes.ts @@ -7,7 +7,6 @@ export interface ModeInfo { } export const DEFAULT_CLAUDE_EXECUTION_MODE: ExecutionMode = "plan"; -export const DEFAULT_CODEX_EXECUTION_MODE: ExecutionMode = "plan"; const availableModes: ModeInfo[] = [ { @@ -53,3 +52,9 @@ export function getAvailableModesForAdapter( ): ModeInfo[] { return adapter === "codex" ? getAvailableCodexModes() : getAvailableModes(); } + +export function getDefaultExecutionModeForAdapter( + adapter: "claude" | "codex", +): ExecutionMode { + return adapter === "codex" ? "auto" : DEFAULT_CLAUDE_EXECUTION_MODE; +} From 3477cd30aeb5b3372faa631292d5e2b1eee98944 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:39:18 +0300 Subject: [PATCH 38/42] fix(mobile): use canonical Codex config Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 16 +++++++++------- .../core/src/sessions/cloudSessionConfig.test.ts | 8 ++++++++ packages/core/src/sessions/cloudSessionConfig.ts | 6 +++++- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index a43104f3de..3f88d541f1 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -1,8 +1,6 @@ import { Text } from "@components/text"; -import { - DEFAULT_CLAUDE_EXECUTION_MODE, - getDefaultExecutionModeForAdapter, -} from "@posthog/core/sessions/executionModes"; +import { getCloudReasoningConfigOptionId } from "@posthog/core/sessions/cloudSessionConfig"; +import { getDefaultExecutionModeForAdapter } from "@posthog/core/sessions/executionModes"; import { countUserMessages, getSessionActivityPhase, @@ -187,7 +185,7 @@ export default function TaskDetailScreen() { : composerConfig.adapter === composerAdapter; const composerMode: ExecutionMode = (composerConfigMatchesAdapter ? composerConfig?.mode : undefined) ?? - DEFAULT_CLAUDE_EXECUTION_MODE; + getDefaultExecutionModeForAdapter(composerAdapter); const composerModel = (composerConfigMatchesAdapter ? composerConfig?.model : undefined) ?? task?.latest_run?.model ?? @@ -566,10 +564,14 @@ export default function TaskDetailScreen() { (value: SupportedReasoningEffort) => { if (!taskId) return; setComposerConfig(taskId, { reasoning: value }); - setConfigOption(taskId, "effort", value).catch(() => {}); + setConfigOption( + taskId, + getCloudReasoningConfigOptionId(composerAdapter), + value, + ).catch(() => {}); usePreferencesStore.getState().setLastUsedReasoningEffort(value); }, - [taskId, setComposerConfig, setConfigOption], + [taskId, composerAdapter, setComposerConfig, setConfigOption], ); const handleStop = useCallback(() => { diff --git a/packages/core/src/sessions/cloudSessionConfig.test.ts b/packages/core/src/sessions/cloudSessionConfig.test.ts index 5a6499f5e4..0b97ebbfcb 100644 --- a/packages/core/src/sessions/cloudSessionConfig.test.ts +++ b/packages/core/src/sessions/cloudSessionConfig.test.ts @@ -4,6 +4,7 @@ import { addMissingCloudRuntimeConfigOptions, buildCloudDefaultConfigOptions, extractLatestConfigOptionsFromEntries, + getCloudReasoningConfigOptionId, } from "./cloudSessionConfig"; function configUpdateEntry( @@ -19,6 +20,13 @@ function configUpdateEntry( } as unknown as StoredLogEntry; } +it.each([ + ["claude", "effort"], + ["codex", "reasoning_effort"], +] as const)("uses the %s reasoning config id", (adapter, expected) => { + expect(getCloudReasoningConfigOptionId(adapter)).toBe(expected); +}); + describe("extractLatestConfigOptionsFromEntries", () => { it("returns undefined when no config_option_update entries exist", () => { expect(extractLatestConfigOptionsFromEntries([])).toBeUndefined(); diff --git a/packages/core/src/sessions/cloudSessionConfig.ts b/packages/core/src/sessions/cloudSessionConfig.ts index 1a7799b3dc..c340613ac4 100644 --- a/packages/core/src/sessions/cloudSessionConfig.ts +++ b/packages/core/src/sessions/cloudSessionConfig.ts @@ -5,6 +5,10 @@ import { getDefaultExecutionModeForAdapter, } from "./executionModes"; +export function getCloudReasoningConfigOptionId(adapter: Adapter): string { + return adapter === "codex" ? "reasoning_effort" : "effort"; +} + /** * Pure derivations of cloud session config options. No store or host access — * just shaping the config-option list the mode switcher renders. @@ -101,7 +105,7 @@ export function addMissingCloudRuntimeConfigOptions( if (initialReasoningEffort && !categories.has("thought_level")) { extras.push({ - id: adapter === "codex" ? "reasoning_effort" : "effort", + id: getCloudReasoningConfigOptionId(adapter), name: adapter === "codex" ? "Reasoning" : "Effort", type: "select", currentValue: initialReasoningEffort, From 588bba69ca84c5a761be5bfcea6abcb75179c360 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:39:25 +0300 Subject: [PATCH 39/42] refactor(mobile): share agent configuration controls Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 27 +-- apps/mobile/src/app/task/index.tsx | 205 ++-------------- .../composer/AgentConfigControls.test.tsx | 97 ++++++++ .../tasks/composer/AgentConfigControls.tsx | 225 ++++++++++++++++++ .../tasks/composer/TaskChatComposer.tsx | 192 ++------------- .../tasks/utils/cloudTaskRunConfig.test.ts | 31 +++ .../tasks/utils/cloudTaskRunConfig.ts | 28 +++ 7 files changed, 432 insertions(+), 373 deletions(-) create mode 100644 apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx create mode 100644 apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx create mode 100644 apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.test.ts create mode 100644 apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.ts diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index 3f88d541f1..2dc24bc565 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -12,7 +12,6 @@ import { DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, - getReasoningEffortOptions, isSupportedReasoningEffort, type SupportedReasoningEffort, serializeCloudPrompt, @@ -61,6 +60,7 @@ import { import { useTaskSessionStore } from "@/features/tasks/stores/taskSessionStore"; import { useTaskStore } from "@/features/tasks/stores/taskStore"; import { confirmStopRun } from "@/features/tasks/utils/archiveGuard"; +import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig"; import { useScreenInsets } from "@/hooks/useScreenInsets"; import { ANALYTICS_EVENTS, @@ -344,18 +344,18 @@ export default function TaskDetailScreen() { ) : text; - const supportsReasoning = - getReasoningEffortOptions(composerAdapter, composerModel) !== null; const updatedTask = await getPostHogApiClient().runTaskInCloud( taskId, undefined, { resumeFromRunId: task.latest_run?.id, pendingUserMessage, - adapter: composerAdapter, - model: composerModel, - reasoningLevel: supportsReasoning ? composerReasoning : undefined, - initialPermissionMode: composerMode, + ...buildCloudTaskRunConfig({ + adapter: composerAdapter, + mode: composerMode, + model: composerModel, + reasoning: composerReasoning, + }), rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, }, ); @@ -623,13 +623,12 @@ export default function TaskDetailScreen() { undefined, { resumeFromRunId: task.latest_run?.id, - adapter: composerAdapter, - model: composerModel, - reasoningLevel: - getReasoningEffortOptions(composerAdapter, composerModel) !== null - ? composerReasoning - : undefined, - initialPermissionMode: composerMode, + ...buildCloudTaskRunConfig({ + adapter: composerAdapter, + mode: composerMode, + model: composerModel, + reasoning: composerReasoning, + }), rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, }, ); diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index 7257cf5240..647094c432 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -2,12 +2,10 @@ import { Text } from "@components/text"; import { DEFAULT_CLAUDE_EXECUTION_MODE, getAvailableModesForAdapter, - getDefaultExecutionModeForAdapter, } from "@posthog/core/sessions/executionModes"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { type Adapter, - DEFAULT_CODEX_MODEL, DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, @@ -20,17 +18,10 @@ import { LinearGradient } from "expo-linear-gradient"; import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { ArrowUp, - BrainIcon, CaretDown, - Cpu, GithubLogo, MicrophoneIcon, PaperclipIcon, - PauseIcon, - PencilIcon, - Robot, - ShieldCheck, - Sparkle, StopIcon, } from "phosphor-react-native"; import { useCallback, useEffect, useState } from "react"; @@ -50,6 +41,7 @@ import { useVoiceRecording } from "@/features/chat"; import { usePreferencesStore } from "@/features/preferences/stores/preferencesStore"; import { GitHubConnectionPrompt } from "@/features/tasks/components/GitHubConnectionPrompt"; import { GitHubLoadNotice } from "@/features/tasks/components/GitHubLoadNotice"; +import { AgentConfigControls } from "@/features/tasks/composer/AgentConfigControls"; import { AttachmentSheet } from "@/features/tasks/composer/attachments/AttachmentSheet"; import { AttachmentsBar } from "@/features/tasks/composer/attachments/AttachmentsBar"; import { buildCloudPromptBlocks } from "@/features/tasks/composer/attachments/buildCloudPrompt"; @@ -61,14 +53,10 @@ import { import type { PendingAttachment } from "@/features/tasks/composer/attachments/types"; import { DotBackground } from "@/features/tasks/composer/DotBackground"; import { - getComposerModelOptions, - getConfigOptionLabel, getMobileExecutionModes, getModelConfigOption, } from "@/features/tasks/composer/options"; -import { Pill } from "@/features/tasks/composer/Pill"; import { RepositoryPickerInline } from "@/features/tasks/composer/RepositoryPickerInline"; -import { SelectSheet } from "@/features/tasks/composer/SelectSheet"; import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskConfigOptions"; import { useUserIntegrations } from "@/features/tasks/hooks/useUserIntegrations"; import { useWarmTask } from "@/features/tasks/hooks/useWarmTask"; @@ -82,6 +70,7 @@ import type { CreateTaskOptions, RepositorySelection, } from "@/features/tasks/types"; +import { buildCloudTaskRunConfig } from "@/features/tasks/utils/cloudTaskRunConfig"; import { findRepositoryOption, isRepositorySelectionComplete, @@ -93,31 +82,12 @@ import { getPostHogApiClient } from "@/lib/posthogApiClient"; import { toRgba, useThemeColors } from "@/lib/theme"; const log = logger.scope("task-create"); -const SWITCH_ADAPTER_VALUE = "__switch_adapter__"; const SUGGESTIONS = [ "Create or update my CLAUDE.md file", "Search for a TODO comment and fix it", "Recommend areas to improve our tests", ] as const; -function modeIcon(mode: ExecutionMode, color: string, size = 14) { - switch (mode) { - case "plan": - return ; - case "default": - return ; - case "acceptEdits": - return ; - case "bypassPermissions": - case "full-access": - return ; - case "read-only": - return ; - case "auto": - return ; - } -} - export default function NewTaskScreen() { const { prompt: initialPrompt, @@ -136,11 +106,7 @@ export default function NewTaskScreen() { const [adapter, setAdapter] = useState("claude"); const { configOptions, hasLiveConfig, isConfigReady } = useCloudTaskConfigOptions(adapter); - const executionModes = getMobileExecutionModes( - getAvailableModesForAdapter(adapter), - ); const modelConfigOption = getModelConfigOption(configOptions); - const mobileModelOptions = getComposerModelOptions(modelConfigOption); const { error, hasGithubIntegration, @@ -236,9 +202,6 @@ export default function NewTaskScreen() { }, [adapter, hasLiveConfig, model, modelConfigOption, reasoning]); const [creating, setCreating] = useState(false); const [repoSheetOpen, setRepoSheetOpen] = useState(false); - const [modeSheetOpen, setModeSheetOpen] = useState(false); - const [modelSheetOpen, setModelSheetOpen] = useState(false); - const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); const [attachments, setAttachments] = useState([]); const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false); @@ -381,15 +344,9 @@ export default function NewTaskScreen() { ) : trimmedPrompt; - const supportsReasoning = - getReasoningEffortOptions(adapter, model) !== null; - await client.runTaskInCloud(task.id, undefined, { pendingUserMessage, - adapter, - model, - reasoningLevel: supportsReasoning ? reasoning : undefined, - initialPermissionMode: mode, + ...buildCloudTaskRunConfig({ adapter, mode, model, reasoning }), autoPublish: usePreferencesStore.getState().autoPublishCloudRuns, rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, ...(signalReport @@ -618,51 +575,27 @@ export default function NewTaskScreen() { paddingRight: 16, }} > - option.id === mode) - ?.name ?? mode - } - accent={mode === "plan"} - onPress={() => setModeSheetOpen(true)} + { + setMode(next); + usePreferencesStore + .getState() + .setLastNewTaskMode(next); + }} + onModelChange={setModel} + onReasoningChange={(next) => { + setReasoning(next); + usePreferencesStore + .getState() + .setLastUsedReasoningEffort(next); + }} /> - - - ) : ( - - ) - } - label={ - getConfigOptionLabel( - modelConfigOption.options, - model, - ) ?? model - } - onPress={() => setModelSheetOpen(true)} - /> - - {showReasoningPill ? ( - - } - label={ - reasoningOptions.find( - (option) => option.value === reasoning, - )?.name ?? reasoning - } - onPress={() => setReasoningSheetOpen(true)} - /> - ) : null} {/* Right-edge fade hints that more pills exist when the row overflows. Non-interactive so taps fall through. */} @@ -771,98 +704,6 @@ export default function NewTaskScreen() { - { - const next = value as ExecutionMode; - setMode(next); - usePreferencesStore.getState().setLastNewTaskMode(next); - }} - onClose={() => setModeSheetOpen(false)} - options={executionModes.map((executionMode) => ({ - value: executionMode.id, - label: executionMode.name, - description: executionMode.description, - icon: modeIcon( - executionMode.id as ExecutionMode, - executionMode.id === "plan" - ? themeColors.accent[11] - : themeColors.gray[11], - 16, - ), - }))} - /> - - { - if (value === SWITCH_ADAPTER_VALUE) { - const nextAdapter: Adapter = - adapter === "claude" ? "codex" : "claude"; - setAdapter(nextAdapter); - setMode(getDefaultExecutionModeForAdapter(nextAdapter)); - setModel( - nextAdapter === "codex" - ? DEFAULT_CODEX_MODEL - : DEFAULT_GATEWAY_MODEL, - ); - setReasoning(DEFAULT_REASONING_EFFORT); - return; - } - const next = resolveCloudComposerModelChange({ - adapter, - modelOption: modelConfigOption, - requestedModel: value, - reasoning, - }); - setModel(next.model); - setReasoning(next.reasoning); - }} - onClose={() => setModelSheetOpen(false)} - options={[ - ...mobileModelOptions.map((modelOption) => ({ - value: modelOption.value, - label: modelOption.label, - description: modelOption.description, - disabled: modelOption.disabled, - icon: - adapter === "codex" ? ( - - ) : ( - - ), - })), - { - value: SWITCH_ADAPTER_VALUE, - label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`, - description: "Change coding agent", - disabled: false, - icon: , - }, - ]} - /> - - { - const next = value as SupportedReasoningEffort; - setReasoning(next); - usePreferencesStore.getState().setLastUsedReasoningEffort(next); - }} - onClose={() => setReasoningSheetOpen(false)} - options={reasoningOptions.map((reasoningLevel) => ({ - value: reasoningLevel.value, - label: reasoningLevel.name, - icon: , - }))} - /> - setAttachmentSheetOpen(false)} diff --git a/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx b/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx new file mode 100644 index 0000000000..33b036c686 --- /dev/null +++ b/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx @@ -0,0 +1,97 @@ +import type { CloudTaskConfigOption } from "@posthog/shared"; +import { createElement, type ReactNode } from "react"; +import { act, create } from "react-test-renderer"; +import { describe, expect, it, vi } from "vitest"; +import { AgentConfigControls } from "./AgentConfigControls"; + +vi.mock("phosphor-react-native", () => { + const icon = (name: string) => (props: Record) => + createElement(name, props); + return { + BrainIcon: icon("BrainIcon"), + CaretDown: icon("CaretDown"), + Check: icon("Check"), + Cpu: icon("Cpu"), + PauseIcon: icon("PauseIcon"), + PencilIcon: icon("PencilIcon"), + Robot: icon("Robot"), + ShieldCheck: icon("ShieldCheck"), + Sparkle: icon("Sparkle"), + }; +}); + +vi.mock("@/components/SheetContainer", () => ({ + SheetContainer: ({ + open, + children, + }: { + open: boolean; + children: ReactNode; + }) => (open ? createElement("SheetContainer", null, children) : null), +})); + +vi.mock("@/lib/theme", () => ({ + useThemeColors: () => ({ + gray: { 10: "#777", 11: "#555" }, + accent: { 9: "#f60", 11: "#f60" }, + }), +})); + +const configOptions: CloudTaskConfigOption[] = [ + { + id: "model", + name: "Model", + type: "select", + currentValue: "claude-sonnet-4-6", + options: [{ value: "claude-sonnet-4-6", name: "Sonnet 4.6" }], + category: "model", + description: "Choose a model", + }, +]; + +function findPressableWithText( + renderer: ReturnType, + label: string, +) { + return renderer.root.find( + (node) => + typeof node.props.onPress === "function" && + node.findAll((child) => child.props.children === label).length > 0, + ); +} + +describe("AgentConfigControls", () => { + it("resets incompatible values when switching adapters", () => { + const onAdapterChange = vi.fn(); + const onModeChange = vi.fn(); + const onModelChange = vi.fn(); + const onReasoningChange = vi.fn(); + let renderer!: ReturnType; + + act(() => { + renderer = create( + createElement(AgentConfigControls, { + adapter: "claude", + mode: "plan", + model: "claude-sonnet-4-6", + reasoning: "high", + configOptions, + onAdapterChange, + onModeChange, + onModelChange, + onReasoningChange, + }), + ); + }); + + act(() => findPressableWithText(renderer, "Sonnet 4.6").props.onPress()); + act(() => + findPressableWithText(renderer, "Switch to Codex").props.onPress(), + ); + + expect(onAdapterChange).toHaveBeenCalledWith("codex"); + expect(onModeChange).toHaveBeenCalledWith("auto"); + expect(onModelChange).toHaveBeenCalledWith("gpt-5.5"); + expect(onReasoningChange).toHaveBeenCalledWith("high"); + }); +}); diff --git a/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx b/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx new file mode 100644 index 0000000000..5d21968dca --- /dev/null +++ b/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx @@ -0,0 +1,225 @@ +import { + getAvailableModesForAdapter, + getDefaultExecutionModeForAdapter, +} from "@posthog/core/sessions/executionModes"; +import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; +import { + type Adapter, + type CloudTaskConfigOption, + DEFAULT_CODEX_MODEL, + DEFAULT_GATEWAY_MODEL, + DEFAULT_REASONING_EFFORT, + type ExecutionMode, + getReasoningEffortOptions, + type SupportedReasoningEffort, +} from "@posthog/shared"; +import { + BrainIcon, + Cpu, + PauseIcon, + PencilIcon, + Robot, + ShieldCheck, + Sparkle, +} from "phosphor-react-native"; +import { type ReactNode, useState } from "react"; +import { useThemeColors } from "@/lib/theme"; +import { + getComposerModelOptions, + getConfigOptionLabel, + getMobileExecutionModes, + getModelConfigOption, +} from "./options"; +import { Pill } from "./Pill"; +import { SelectSheet } from "./SelectSheet"; + +const SWITCH_ADAPTER_VALUE = "__switch_adapter__"; + +interface AgentConfigControlsProps { + adapter: Adapter; + mode: ExecutionMode; + model: string; + reasoning: SupportedReasoningEffort; + configOptions: readonly CloudTaskConfigOption[]; + onAdapterChange: (adapter: Adapter) => void; + onModeChange: (mode: ExecutionMode) => void; + onModelChange: (model: string) => void; + onReasoningChange: (reasoning: SupportedReasoningEffort) => void; + canChangeAdapter?: boolean; +} + +function modeIcon(mode: ExecutionMode, color: string, size = 14): ReactNode { + switch (mode) { + case "plan": + return ; + case "default": + return ; + case "acceptEdits": + return ; + case "bypassPermissions": + case "full-access": + return ; + case "read-only": + return ; + case "auto": + return ; + } +} + +export function AgentConfigControls({ + adapter, + mode, + model, + reasoning, + configOptions, + onAdapterChange, + onModeChange, + onModelChange, + onReasoningChange, + canChangeAdapter = true, +}: AgentConfigControlsProps) { + const themeColors = useThemeColors(); + const [modeSheetOpen, setModeSheetOpen] = useState(false); + const [modelSheetOpen, setModelSheetOpen] = useState(false); + const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); + const executionModes = getMobileExecutionModes( + getAvailableModesForAdapter(adapter), + ); + const modelConfigOption = getModelConfigOption(configOptions); + const modelOptions = getComposerModelOptions(modelConfigOption); + const reasoningOptions = getReasoningEffortOptions(adapter, model) ?? []; + + return ( + <> + option.id === mode)?.name ?? mode + } + accent={mode === "plan"} + onPress={() => setModeSheetOpen(true)} + /> + + + ) : ( + + ) + } + label={getConfigOptionLabel(modelConfigOption.options, model) ?? model} + onPress={() => setModelSheetOpen(true)} + /> + + {reasoningOptions.length > 0 ? ( + } + label={ + reasoningOptions.find((option) => option.value === reasoning) + ?.name ?? reasoning + } + onPress={() => setReasoningSheetOpen(true)} + /> + ) : null} + + onModeChange(value as ExecutionMode)} + onClose={() => setModeSheetOpen(false)} + options={executionModes.map((option) => ({ + value: option.id, + label: option.name, + description: option.description, + icon: modeIcon( + option.id as ExecutionMode, + option.id === "plan" + ? themeColors.accent[11] + : themeColors.gray[11], + 16, + ), + }))} + /> + + { + if (value === SWITCH_ADAPTER_VALUE) { + const nextAdapter: Adapter = + adapter === "claude" ? "codex" : "claude"; + onAdapterChange(nextAdapter); + onModeChange(getDefaultExecutionModeForAdapter(nextAdapter)); + onModelChange( + nextAdapter === "codex" + ? DEFAULT_CODEX_MODEL + : DEFAULT_GATEWAY_MODEL, + ); + onReasoningChange(DEFAULT_REASONING_EFFORT); + return; + } + const next = resolveCloudComposerModelChange({ + adapter, + modelOption: modelConfigOption, + requestedModel: value, + reasoning, + }); + onModelChange(next.model); + onReasoningChange(next.reasoning); + }} + onClose={() => setModelSheetOpen(false)} + options={[ + ...modelOptions.map((option) => ({ + value: option.value, + label: option.label, + description: option.description, + disabled: option.disabled, + icon: + adapter === "codex" ? ( + + ) : ( + + ), + })), + ...(canChangeAdapter + ? [ + { + value: SWITCH_ADAPTER_VALUE, + label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`, + description: "Change coding agent", + disabled: false, + icon: + adapter === "claude" ? ( + + ) : ( + + ), + }, + ] + : []), + ]} + /> + + + onReasoningChange(value as SupportedReasoningEffort) + } + onClose={() => setReasoningSheetOpen(false)} + options={reasoningOptions.map((option) => ({ + value: option.value, + label: option.name, + icon: , + }))} + /> + + ); +} diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index 25eda788fa..1d551d7488 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -1,35 +1,24 @@ import { Text } from "@components/text"; -import { - DEFAULT_CLAUDE_EXECUTION_MODE, - getAvailableModesForAdapter, - getDefaultExecutionModeForAdapter, -} from "@posthog/core/sessions/executionModes"; +import { DEFAULT_CLAUDE_EXECUTION_MODE } from "@posthog/core/sessions/executionModes"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { type Adapter, DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, type ExecutionMode, - getReasoningEffortOptions, type SupportedReasoningEffort, } from "@posthog/shared"; import * as Haptics from "expo-haptics"; import { ArrowUp, - BrainIcon, - Cpu, Lightning, Microphone, PaperclipIcon, - PauseIcon, PencilIcon, - Robot, - ShieldCheck, - Sparkle, Stack, Stop, } from "phosphor-react-native"; -import { type ReactNode, useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { ActivityIndicator, Keyboard, @@ -43,6 +32,7 @@ import { useCloudTaskConfigOptions } from "@/features/tasks/hooks/useCloudTaskCo import { logger } from "@/lib/logger"; import { useThemeColors } from "@/lib/theme"; import type { MessagingMode } from "../stores/messagingModeStore"; +import { AgentConfigControls } from "./AgentConfigControls"; import { AttachmentSheet } from "./attachments/AttachmentSheet"; import { AttachmentsBar } from "./attachments/AttachmentsBar"; import { @@ -51,15 +41,8 @@ import { pickPhotoFromLibrary, } from "./attachments/pickers"; import type { PendingAttachment } from "./attachments/types"; -import { - getComposerModelOptions, - getConfigOptionLabel, - getMobileExecutionModes, - getModelConfigOption, - resolveComposerPrimaryAction, -} from "./options"; +import { getModelConfigOption, resolveComposerPrimaryAction } from "./options"; import { Pill } from "./Pill"; -import { SelectSheet } from "./SelectSheet"; import { type ComposerContent, isComposerEmpty, @@ -67,7 +50,6 @@ import { } from "./submitComposerMessage"; const log = logger.scope("task-chat-composer"); -const SWITCH_ADAPTER_VALUE = "__switch_adapter__"; interface TaskChatComposerProps { onSend: ( message: string, @@ -99,24 +81,6 @@ interface TaskChatComposerProps { onCancelEdit?: () => void; } -function modeIcon(mode: ExecutionMode, color: string, size = 14): ReactNode { - switch (mode) { - case "plan": - return ; - case "default": - return ; - case "acceptEdits": - return ; - case "bypassPermissions": - case "full-access": - return ; - case "read-only": - return ; - case "auto": - return ; - } -} - export function TaskChatComposer({ onSend, onStop, @@ -142,11 +106,7 @@ export function TaskChatComposer({ }: TaskChatComposerProps) { const themeColors = useThemeColors(); const { configOptions, hasLiveConfig } = useCloudTaskConfigOptions(adapter); - const executionModes = getMobileExecutionModes( - getAvailableModesForAdapter(adapter), - ); const modelConfigOption = getModelConfigOption(configOptions); - const mobileModelOptions = getComposerModelOptions(modelConfigOption); const [message, setMessage] = useState(() => initialMessage ?? ""); const [attachments, setAttachments] = useState([]); const [attachmentSheetOpen, setAttachmentSheetOpen] = useState(false); @@ -200,13 +160,6 @@ export function TaskChatComposer({ const isRecording = status === "recording"; const isTranscribing = status === "transcribing"; - const [modeSheetOpen, setModeSheetOpen] = useState(false); - const [modelSheetOpen, setModelSheetOpen] = useState(false); - const [reasoningSheetOpen, setReasoningSheetOpen] = useState(false); - - const reasoningOptions = getReasoningEffortOptions(adapter, model) ?? []; - const showReasoningPill = reasoningOptions.length > 0; - const hasContent = !isComposerEmpty({ text: message, attachments }); const primaryAction = resolveComposerPrimaryAction({ hasContent, @@ -366,48 +319,19 @@ export function TaskChatComposer({ paddingRight: 4, }} > - option.id === mode)?.name ?? - mode - } - accent={mode === "plan"} - onPress={() => setModeSheetOpen(true)} + - - ) : ( - - ) - } - label={ - getConfigOptionLabel(modelConfigOption.options, model) ?? - model - } - onPress={() => setModelSheetOpen(true)} - /> - - {showReasoningPill ? ( - } - label={ - reasoningOptions.find( - (option) => option.value === reasoning, - )?.name ?? reasoning - } - onPress={() => setReasoningSheetOpen(true)} - /> - ) : null} - - onModeChange(v as ExecutionMode)} - onClose={() => setModeSheetOpen(false)} - options={executionModes.map((m) => ({ - value: m.id, - label: m.name, - description: m.description, - icon: modeIcon( - m.id as ExecutionMode, - m.id === "plan" ? themeColors.accent[11] : themeColors.gray[11], - 16, - ), - }))} - /> - - { - if (v === SWITCH_ADAPTER_VALUE) { - const nextAdapter = adapter === "claude" ? "codex" : "claude"; - onAdapterChange(nextAdapter); - onModeChange(getDefaultExecutionModeForAdapter(nextAdapter)); - return; - } - const next = resolveCloudComposerModelChange({ - adapter, - modelOption: modelConfigOption, - requestedModel: v, - reasoning, - }); - onModelChange(next.model); - if (next.reasoning !== reasoning) { - onReasoningChange(next.reasoning); - } - }} - onClose={() => setModelSheetOpen(false)} - options={[ - ...mobileModelOptions.map((m) => ({ - value: m.value, - label: m.label, - description: m.description, - disabled: m.disabled, - icon: - adapter === "codex" ? ( - - ) : ( - - ), - })), - ...(canChangeAdapter - ? [ - { - value: SWITCH_ADAPTER_VALUE, - label: `Switch to ${adapter === "claude" ? "Codex" : "Claude Code"}`, - description: "Change coding agent", - disabled: false, - icon: - adapter === "claude" ? ( - - ) : ( - - ), - }, - ] - : []), - ]} - /> - - onReasoningChange(v as SupportedReasoningEffort)} - onClose={() => setReasoningSheetOpen(false)} - options={reasoningOptions.map((r) => ({ - value: r.value, - label: r.name, - icon: , - }))} - /> - setAttachmentSheetOpen(false)} diff --git a/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.test.ts b/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.test.ts new file mode 100644 index 0000000000..320a2f6a56 --- /dev/null +++ b/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { buildCloudTaskRunConfig } from "./cloudTaskRunConfig"; + +describe("buildCloudTaskRunConfig", () => { + it("forwards the selected Codex configuration to cloud task dispatch", () => { + expect( + buildCloudTaskRunConfig({ + adapter: "codex", + mode: "full-access", + model: "gpt-5.5", + reasoning: "high", + }), + ).toEqual({ + adapter: "codex", + initialPermissionMode: "full-access", + model: "gpt-5.5", + reasoningLevel: "high", + }); + }); + + it("omits reasoning when the selected model does not support it", () => { + expect( + buildCloudTaskRunConfig({ + adapter: "claude", + mode: "plan", + model: "claude-haiku-4-5", + reasoning: "high", + }).reasoningLevel, + ).toBeUndefined(); + }); +}); diff --git a/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.ts b/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.ts new file mode 100644 index 0000000000..0ebddf8c4b --- /dev/null +++ b/apps/mobile/src/features/tasks/utils/cloudTaskRunConfig.ts @@ -0,0 +1,28 @@ +import { + type Adapter, + type ExecutionMode, + getReasoningEffortOptions, + type SupportedReasoningEffort, +} from "@posthog/shared"; + +export function buildCloudTaskRunConfig({ + adapter, + mode, + model, + reasoning, +}: { + adapter: Adapter; + mode: ExecutionMode; + model: string; + reasoning: SupportedReasoningEffort; +}) { + return { + adapter, + model, + reasoningLevel: + getReasoningEffortOptions(adapter, model) === null + ? undefined + : reasoning, + initialPermissionMode: mode, + }; +} From a6551d684b597b8a4e0504d9b49faa07b903467d Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Fri, 24 Jul 2026 19:39:32 +0300 Subject: [PATCH 40/42] fix(mobile): validate resumed agent controls Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- .../tasks/stores/taskSessionStore.test.ts | 35 +++++++++++++++++-- .../features/tasks/stores/taskSessionStore.ts | 16 +++++++-- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts index c38504a1c3..2aadd16883 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts @@ -263,6 +263,8 @@ describe("_resumeCloudRun", () => { mockGetTask.mockResolvedValue( previousTask({ branch: "feature", + runtime_adapter: "claude", + model: "claude-opus-4-8", reasoning_effort: "low", state: { initial_permission_mode: "acceptEdits" }, }), @@ -275,6 +277,7 @@ describe("_resumeCloudRun", () => { expect(mockRunTaskInCloud).toHaveBeenCalledWith("t1", { branch: "feature", runtimeAdapter: "claude", + model: "claude-opus-4-8", resumeFromRunId: "prev-run", pendingUserMessage: "hi", reasoningEffort: "low", @@ -299,7 +302,14 @@ describe("_resumeCloudRun", () => { it("prefers the composer's current selection over the previous run", async () => { useTaskStore.setState({ - composerConfigByTaskId: { t1: { mode: "plan", reasoning: "max" } }, + composerConfigByTaskId: { + t1: { + adapter: "codex", + mode: "plan", + model: "gpt-5.5", + reasoning: "high", + }, + }, }); mockGetTask.mockResolvedValue( previousTask({ @@ -316,11 +326,32 @@ describe("_resumeCloudRun", () => { expect(mockRunTaskInCloud).toHaveBeenCalledWith( "t1", expect.objectContaining({ - reasoningEffort: "max", + runtimeAdapter: "codex", + model: "gpt-5.5", + reasoningEffort: "high", initialPermissionMode: "plan", }), ); }); + + it("drops stored reasoning unsupported by the resumed model", async () => { + mockGetTask.mockResolvedValue( + previousTask({ + runtime_adapter: "claude", + model: "claude-sonnet-4-6", + reasoning_effort: "max", + }), + ); + + await useTaskSessionStore + .getState() + ._resumeCloudRun("t1", "prev-run", "hi"); + + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "t1", + expect.objectContaining({ reasoningEffort: undefined }), + ); + }); }); describe("compaction tracking from the log stream", () => { diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts index 0c57cc6d1a..539545dc4a 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts @@ -1,6 +1,8 @@ import { convertStoredEntriesToPortableSessionEvents } from "@posthog/core/sessions/portableSessionEvents"; import { + type Adapter, type CloudTaskUpdatePayload, + isSupportedReasoningEffort, isTerminalStatus, type StoredLogEntry, serializeCloudPrompt, @@ -1187,9 +1189,18 @@ export const useTaskSessionStore = create((set, get) => ({ const composerConfig = useTaskStore.getState().composerConfigByTaskId[taskId]; + const adapter: Adapter = + composerConfig?.adapter ?? previousRun?.runtime_adapter ?? "claude"; + const model = composerConfig?.model ?? previousRun?.model ?? undefined; const previousPermissionMode = previousRun?.state?.initial_permission_mode; - const reasoningEffort = + const requestedReasoning = composerConfig?.reasoning ?? previousRun?.reasoning_effort ?? undefined; + const reasoningEffort = + model && + requestedReasoning && + isSupportedReasoningEffort(adapter, model, requestedReasoning) + ? requestedReasoning + : undefined; const initialPermissionMode = composerConfig?.mode ?? (typeof previousPermissionMode === "string" @@ -1198,7 +1209,8 @@ export const useTaskSessionStore = create((set, get) => ({ const updatedTask = await runTaskInCloud(taskId, { branch: previousBranch, - runtimeAdapter: "claude", + runtimeAdapter: adapter, + model, resumeFromRunId: previousRunId, pendingUserMessage: prompt, reasoningEffort, From 1c3bc6c9a5271ed309f1f49964356315a9a4ac12 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 02:37:00 +0300 Subject: [PATCH 41/42] fix(mobile): centralize agent config transitions Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- apps/mobile/src/app/task/[id].tsx | 13 +++-- apps/mobile/src/app/task/index.tsx | 12 ++++- .../composer/AgentConfigControls.test.tsx | 38 ++++++++++++-- .../tasks/composer/AgentConfigControls.tsx | 25 +++------- .../tasks/composer/TaskChatComposer.tsx | 3 +- .../tasks/stores/taskSessionStore.test.ts | 30 ++++++++++++ .../features/tasks/stores/taskSessionStore.ts | 32 ++++-------- .../core/src/sessions/cloudRunOptions.test.ts | 40 +++++++++++++++ packages/core/src/sessions/cloudRunOptions.ts | 49 +++++++++++++++++++ .../task-detail/composerModelPolicy.test.ts | 26 +++++++++- .../src/task-detail/composerModelPolicy.ts | 23 +++++++++ 11 files changed, 236 insertions(+), 55 deletions(-) diff --git a/apps/mobile/src/app/task/[id].tsx b/apps/mobile/src/app/task/[id].tsx index 2dc24bc565..086ba8efa2 100644 --- a/apps/mobile/src/app/task/[id].tsx +++ b/apps/mobile/src/app/task/[id].tsx @@ -5,6 +5,7 @@ import { countUserMessages, getSessionActivityPhase, } from "@posthog/core/sessions/sessionActivity"; +import type { CloudComposerSelection } from "@posthog/core/task-detail/composerModelPolicy"; import { isTaskRunning } from "@posthog/core/tasks/taskArchive"; import { type Adapter, @@ -539,14 +540,12 @@ export default function TaskDetailScreen() { ); const handleAdapterChange = useCallback( - (value: Adapter) => { + (selection: CloudComposerSelection) => { if (!taskId) return; - setComposerConfig(taskId, { - adapter: value, - mode: getDefaultExecutionModeForAdapter(value), - model: value === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL, - reasoning: DEFAULT_REASONING_EFFORT, - }); + setComposerConfig(taskId, selection); + const preferences = usePreferencesStore.getState(); + preferences.setLastNewTaskMode(selection.mode); + preferences.setLastUsedReasoningEffort(selection.reasoning); }, [taskId, setComposerConfig], ); diff --git a/apps/mobile/src/app/task/index.tsx b/apps/mobile/src/app/task/index.tsx index 647094c432..fe260587eb 100644 --- a/apps/mobile/src/app/task/index.tsx +++ b/apps/mobile/src/app/task/index.tsx @@ -581,7 +581,17 @@ export default function NewTaskScreen() { model={model} reasoning={reasoning} configOptions={configOptions} - onAdapterChange={setAdapter} + onAdapterChange={(next) => { + setAdapter(next.adapter); + setMode(next.mode); + setModel(next.model); + setReasoning(next.reasoning); + const preferences = usePreferencesStore.getState(); + preferences.setLastNewTaskMode(next.mode); + preferences.setLastUsedReasoningEffort( + next.reasoning, + ); + }} onModeChange={(next) => { setMode(next); usePreferencesStore diff --git a/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx b/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx index 33b036c686..3516aa7c75 100644 --- a/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx +++ b/apps/mobile/src/features/tasks/composer/AgentConfigControls.test.tsx @@ -89,9 +89,39 @@ describe("AgentConfigControls", () => { findPressableWithText(renderer, "Switch to Codex").props.onPress(), ); - expect(onAdapterChange).toHaveBeenCalledWith("codex"); - expect(onModeChange).toHaveBeenCalledWith("auto"); - expect(onModelChange).toHaveBeenCalledWith("gpt-5.5"); - expect(onReasoningChange).toHaveBeenCalledWith("high"); + expect(onAdapterChange).toHaveBeenCalledWith({ + adapter: "codex", + mode: "auto", + model: "gpt-5.5", + reasoning: "high", + }); + expect(onModeChange).not.toHaveBeenCalled(); + expect(onModelChange).not.toHaveBeenCalled(); + expect(onReasoningChange).not.toHaveBeenCalled(); + }); + + it("hides adapter switching while the active run locks the adapter", () => { + let renderer!: ReturnType; + + act(() => { + renderer = create( + createElement(AgentConfigControls, { + adapter: "claude", + mode: "plan", + model: "claude-sonnet-4-6", + reasoning: "high", + configOptions, + canChangeAdapter: false, + onAdapterChange: vi.fn(), + onModeChange: vi.fn(), + onModelChange: vi.fn(), + onReasoningChange: vi.fn(), + }), + ); + }); + + act(() => findPressableWithText(renderer, "Sonnet 4.6").props.onPress()); + + expect(() => findPressableWithText(renderer, "Switch to Codex")).toThrow(); }); }); diff --git a/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx b/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx index 5d21968dca..8f5d1aa360 100644 --- a/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx +++ b/apps/mobile/src/features/tasks/composer/AgentConfigControls.tsx @@ -1,14 +1,12 @@ +import { getAvailableModesForAdapter } from "@posthog/core/sessions/executionModes"; import { - getAvailableModesForAdapter, - getDefaultExecutionModeForAdapter, -} from "@posthog/core/sessions/executionModes"; -import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; + type CloudComposerSelection, + resolveCloudComposerAdapterChange, + resolveCloudComposerModelChange, +} from "@posthog/core/task-detail/composerModelPolicy"; import { type Adapter, type CloudTaskConfigOption, - DEFAULT_CODEX_MODEL, - DEFAULT_GATEWAY_MODEL, - DEFAULT_REASONING_EFFORT, type ExecutionMode, getReasoningEffortOptions, type SupportedReasoningEffort, @@ -41,7 +39,7 @@ interface AgentConfigControlsProps { model: string; reasoning: SupportedReasoningEffort; configOptions: readonly CloudTaskConfigOption[]; - onAdapterChange: (adapter: Adapter) => void; + onAdapterChange: (selection: CloudComposerSelection) => void; onModeChange: (mode: ExecutionMode) => void; onModelChange: (model: string) => void; onReasoningChange: (reasoning: SupportedReasoningEffort) => void; @@ -152,16 +150,7 @@ export function AgentConfigControls({ value={model} onChange={(value) => { if (value === SWITCH_ADAPTER_VALUE) { - const nextAdapter: Adapter = - adapter === "claude" ? "codex" : "claude"; - onAdapterChange(nextAdapter); - onModeChange(getDefaultExecutionModeForAdapter(nextAdapter)); - onModelChange( - nextAdapter === "codex" - ? DEFAULT_CODEX_MODEL - : DEFAULT_GATEWAY_MODEL, - ); - onReasoningChange(DEFAULT_REASONING_EFFORT); + onAdapterChange(resolveCloudComposerAdapterChange(adapter)); return; } const next = resolveCloudComposerModelChange({ diff --git a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx index 1d551d7488..611cd9f635 100644 --- a/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx +++ b/apps/mobile/src/features/tasks/composer/TaskChatComposer.tsx @@ -1,5 +1,6 @@ import { Text } from "@components/text"; import { DEFAULT_CLAUDE_EXECUTION_MODE } from "@posthog/core/sessions/executionModes"; +import type { CloudComposerSelection } from "@posthog/core/task-detail/composerModelPolicy"; import { resolveCloudComposerModelChange } from "@posthog/core/task-detail/composerModelPolicy"; import { type Adapter, @@ -65,7 +66,7 @@ interface TaskChatComposerProps { mode: ExecutionMode; model: string; reasoning: SupportedReasoningEffort; - onAdapterChange: (adapter: Adapter) => void; + onAdapterChange: (selection: CloudComposerSelection) => void; canChangeAdapter?: boolean; onModeChange: (mode: ExecutionMode) => void; onModelChange: (model: string) => void; diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts index 2aadd16883..a5000a8e21 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.test.ts @@ -352,6 +352,36 @@ describe("_resumeCloudRun", () => { expect.objectContaining({ reasoningEffort: undefined }), ); }); + + it("ignores legacy Claude composer values when resuming Codex", async () => { + useTaskStore.setState({ + composerConfigByTaskId: { + t1: { mode: "plan", model: "claude-opus-4-8", reasoning: "high" }, + }, + }); + mockGetTask.mockResolvedValue( + previousTask({ + runtime_adapter: "codex", + model: "gpt-5.5", + reasoning_effort: "medium", + state: { initial_permission_mode: "auto" }, + }), + ); + + await useTaskSessionStore + .getState() + ._resumeCloudRun("t1", "prev-run", "hi"); + + expect(mockRunTaskInCloud).toHaveBeenCalledWith( + "t1", + expect.objectContaining({ + runtimeAdapter: "codex", + model: "gpt-5.5", + reasoningEffort: "medium", + initialPermissionMode: "auto", + }), + ); + }); }); describe("compaction tracking from the log stream", () => { diff --git a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts index 539545dc4a..83e191119f 100644 --- a/apps/mobile/src/features/tasks/stores/taskSessionStore.ts +++ b/apps/mobile/src/features/tasks/stores/taskSessionStore.ts @@ -1,8 +1,7 @@ +import { resolveCloudResumeOptions } from "@posthog/core/sessions/cloudRunOptions"; import { convertStoredEntriesToPortableSessionEvents } from "@posthog/core/sessions/portableSessionEvents"; import { - type Adapter, type CloudTaskUpdatePayload, - isSupportedReasoningEffort, isTerminalStatus, type StoredLogEntry, serializeCloudPrompt, @@ -1189,32 +1188,19 @@ export const useTaskSessionStore = create((set, get) => ({ const composerConfig = useTaskStore.getState().composerConfigByTaskId[taskId]; - const adapter: Adapter = - composerConfig?.adapter ?? previousRun?.runtime_adapter ?? "claude"; - const model = composerConfig?.model ?? previousRun?.model ?? undefined; - const previousPermissionMode = previousRun?.state?.initial_permission_mode; - const requestedReasoning = - composerConfig?.reasoning ?? previousRun?.reasoning_effort ?? undefined; - const reasoningEffort = - model && - requestedReasoning && - isSupportedReasoningEffort(adapter, model, requestedReasoning) - ? requestedReasoning - : undefined; - const initialPermissionMode = - composerConfig?.mode ?? - (typeof previousPermissionMode === "string" - ? previousPermissionMode - : undefined); + const runtimeOptions = resolveCloudResumeOptions( + composerConfig, + previousRun, + ); const updatedTask = await runTaskInCloud(taskId, { branch: previousBranch, - runtimeAdapter: adapter, - model, + runtimeAdapter: runtimeOptions.adapter, + model: runtimeOptions.model, resumeFromRunId: previousRunId, pendingUserMessage: prompt, - reasoningEffort, - initialPermissionMode, + reasoningEffort: runtimeOptions.reasoningLevel, + initialPermissionMode: runtimeOptions.initialPermissionMode, rtkEnabled: usePreferencesStore.getState().rtkEnabledCloud, }); diff --git a/packages/core/src/sessions/cloudRunOptions.test.ts b/packages/core/src/sessions/cloudRunOptions.test.ts index 4ee64d8db2..cdca88495a 100644 --- a/packages/core/src/sessions/cloudRunOptions.test.ts +++ b/packages/core/src/sessions/cloudRunOptions.test.ts @@ -5,6 +5,7 @@ import { getCloudPrAuthorshipMode, getCloudRunSource, getCloudRuntimeOptions, + resolveCloudResumeOptions, } from "./cloudRunOptions"; describe("getCloudPrAuthorshipMode", () => { @@ -115,3 +116,42 @@ describe("getCloudRuntimeOptions", () => { expect(result.initialPermissionMode).toBe(expected); }); }); + +describe("resolveCloudResumeOptions", () => { + it("ignores legacy Claude composer values when resuming Codex", () => { + expect( + resolveCloudResumeOptions( + { model: "claude-opus-4-8", reasoning: "high", mode: "plan" }, + { + runtime_adapter: "codex", + model: "gpt-5.5", + reasoning_effort: "medium", + state: { initial_permission_mode: "auto" }, + } as unknown as TaskRun, + ), + ).toEqual({ + adapter: "codex", + model: "gpt-5.5", + reasoningLevel: "medium", + initialPermissionMode: "auto", + }); + }); + + it("does not carry previous run options across an explicit adapter change", () => { + expect( + resolveCloudResumeOptions( + { adapter: "codex", model: "gpt-5.5", reasoning: "high", mode: "auto" }, + { + runtime_adapter: "claude", + model: "claude-opus-4-8", + state: { initial_permission_mode: "plan" }, + } as unknown as TaskRun, + ), + ).toEqual({ + adapter: "codex", + model: "gpt-5.5", + reasoningLevel: "high", + initialPermissionMode: "auto", + }); + }); +}); diff --git a/packages/core/src/sessions/cloudRunOptions.ts b/packages/core/src/sessions/cloudRunOptions.ts index 402d936f00..18381cbcc1 100644 --- a/packages/core/src/sessions/cloudRunOptions.ts +++ b/packages/core/src/sessions/cloudRunOptions.ts @@ -4,7 +4,9 @@ import { type CloudRunSource, type ExecutionMode, getConfigOptionByCategory, + isSupportedReasoningEffort, type PrAuthorshipMode, + type SupportedReasoningEffort, } from "@posthog/shared"; import type { TaskRun } from "@posthog/shared/domain-types"; @@ -38,6 +40,53 @@ export interface CloudRuntimeOptions { initialPermissionMode?: ExecutionMode; } +export interface StoredCloudComposerConfig { + adapter?: Adapter; + model?: string; + reasoning?: SupportedReasoningEffort; + mode?: ExecutionMode; +} + +export function resolveCloudResumeOptions( + composerConfig: StoredCloudComposerConfig | undefined, + previousRun: TaskRun | undefined, +): Required> & + Omit { + const adapter = + composerConfig?.adapter ?? previousRun?.runtime_adapter ?? "claude"; + const composerAdapter = composerConfig?.adapter ?? "claude"; + const useComposerConfig = + composerConfig !== undefined && composerAdapter === adapter; + const previousRunMatchesAdapter = + previousRun?.runtime_adapter === undefined || + previousRun.runtime_adapter === adapter; + const model = + (useComposerConfig ? composerConfig.model : undefined) ?? + (previousRunMatchesAdapter ? previousRun?.model : undefined) ?? + undefined; + const requestedReasoning = + (useComposerConfig ? composerConfig.reasoning : undefined) ?? + (previousRunMatchesAdapter ? previousRun?.reasoning_effort : undefined) ?? + undefined; + const previousMode = previousRun?.state?.initial_permission_mode; + + return { + adapter, + model, + reasoningLevel: + model && + requestedReasoning && + isSupportedReasoningEffort(adapter, model, requestedReasoning) + ? requestedReasoning + : undefined, + initialPermissionMode: + (useComposerConfig ? composerConfig.mode : undefined) ?? + (previousRunMatchesAdapter && typeof previousMode === "string" + ? (previousMode as ExecutionMode) + : undefined), + }; +} + export function getCloudRuntimeOptions( session: AgentSession, previousRun?: TaskRun, diff --git a/packages/core/src/task-detail/composerModelPolicy.test.ts b/packages/core/src/task-detail/composerModelPolicy.test.ts index 235c6199f6..d95d47ee5e 100644 --- a/packages/core/src/task-detail/composerModelPolicy.test.ts +++ b/packages/core/src/task-detail/composerModelPolicy.test.ts @@ -6,7 +6,31 @@ import { type SupportedReasoningEffort, } from "@posthog/shared"; import { expect, it } from "vitest"; -import { resolveCloudComposerModelChange } from "./composerModelPolicy"; +import { + resolveCloudComposerAdapterChange, + resolveCloudComposerModelChange, +} from "./composerModelPolicy"; + +it.each([ + [ + "claude", + { adapter: "codex", mode: "auto", model: "gpt-5.5", reasoning: "high" }, + ], + [ + "codex", + { + adapter: "claude", + mode: "plan", + model: DEFAULT_GATEWAY_MODEL, + reasoning: "high", + }, + ], +] as const)( + "resets composer defaults when switching from %s", + (adapter, expected) => { + expect(resolveCloudComposerAdapterChange(adapter)).toEqual(expected); + }, +); const modelOption: CloudTaskConfigOption = { id: "model", diff --git a/packages/core/src/task-detail/composerModelPolicy.ts b/packages/core/src/task-detail/composerModelPolicy.ts index d9783335f2..1c496e5ce1 100644 --- a/packages/core/src/task-detail/composerModelPolicy.ts +++ b/packages/core/src/task-detail/composerModelPolicy.ts @@ -1,11 +1,34 @@ import { type Adapter, type CloudTaskConfigOption, + DEFAULT_CODEX_MODEL, + DEFAULT_GATEWAY_MODEL, DEFAULT_REASONING_EFFORT, + type ExecutionMode, isRestrictedModelOption, isSupportedReasoningEffort, type SupportedReasoningEffort, } from "@posthog/shared"; +import { getDefaultExecutionModeForAdapter } from "../sessions/executionModes"; + +export interface CloudComposerSelection { + adapter: Adapter; + mode: ExecutionMode; + model: string; + reasoning: SupportedReasoningEffort; +} + +export function resolveCloudComposerAdapterChange( + currentAdapter: Adapter, +): CloudComposerSelection { + const adapter: Adapter = currentAdapter === "claude" ? "codex" : "claude"; + return { + adapter, + mode: getDefaultExecutionModeForAdapter(adapter), + model: adapter === "codex" ? DEFAULT_CODEX_MODEL : DEFAULT_GATEWAY_MODEL, + reasoning: DEFAULT_REASONING_EFFORT, + }; +} export function resolveCloudComposerModelChange({ adapter, From 9d4a2e49f6752ca19f6f6f606e449a724f4988b1 Mon Sep 17 00:00:00 2001 From: Richard Solomou Date: Sat, 25 Jul 2026 02:38:19 +0300 Subject: [PATCH 42/42] fix(core): treat legacy runs as Claude Generated-By: PostHog Code Task-Id: c1bbe3cf-742b-4b24-bf96-d11a18b4cf22 --- packages/core/src/sessions/cloudRunOptions.test.ts | 1 - packages/core/src/sessions/cloudRunOptions.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/core/src/sessions/cloudRunOptions.test.ts b/packages/core/src/sessions/cloudRunOptions.test.ts index cdca88495a..53f6a69fbc 100644 --- a/packages/core/src/sessions/cloudRunOptions.test.ts +++ b/packages/core/src/sessions/cloudRunOptions.test.ts @@ -142,7 +142,6 @@ describe("resolveCloudResumeOptions", () => { resolveCloudResumeOptions( { adapter: "codex", model: "gpt-5.5", reasoning: "high", mode: "auto" }, { - runtime_adapter: "claude", model: "claude-opus-4-8", state: { initial_permission_mode: "plan" }, } as unknown as TaskRun, diff --git a/packages/core/src/sessions/cloudRunOptions.ts b/packages/core/src/sessions/cloudRunOptions.ts index 18381cbcc1..4beec4d0ac 100644 --- a/packages/core/src/sessions/cloudRunOptions.ts +++ b/packages/core/src/sessions/cloudRunOptions.ts @@ -57,9 +57,9 @@ export function resolveCloudResumeOptions( const composerAdapter = composerConfig?.adapter ?? "claude"; const useComposerConfig = composerConfig !== undefined && composerAdapter === adapter; + const previousAdapter = previousRun?.runtime_adapter ?? "claude"; const previousRunMatchesAdapter = - previousRun?.runtime_adapter === undefined || - previousRun.runtime_adapter === adapter; + previousRun !== undefined && previousAdapter === adapter; const model = (useComposerConfig ? composerConfig.model : undefined) ?? (previousRunMatchesAdapter ? previousRun?.model : undefined) ??