diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 778c3d13f7917..fded4eec9e417 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -24,7 +24,7 @@ "@t3tools/tailscale": "workspace:*", "dbus-next": "0.10.2", "effect": "catalog:", - "electron": "44.4.5", + "electron": "44.9.3", "electron-store": "^8.2.0", "electron-updater": "^6.8.9", "electron-webauthn": "catalog:", diff --git a/apps/server/src/mcp/OrchestratorMcpService.test.ts b/apps/server/src/mcp/OrchestratorMcpService.test.ts index c037370d45195..015cf1f576964 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.test.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.test.ts @@ -420,6 +420,7 @@ describe("OrchestratorMcpService", () => { : Effect.succeed(childProjection), getThreadShell: (threadId) => Effect.succeed(liveThreadShell(threadId)), delegatedTaskResultPending: () => Effect.succeed(false), + stopDelegatedTasks: () => Effect.void, // The orchestrator's subagent.stop repair is covered in ThreadStop.test.ts. dispatch: (command) => Ref.update(dispatched, (commands) => [...commands, command]).pipe( @@ -458,9 +459,15 @@ describe("OrchestratorMcpService", () => { clientRequestId: "cancel-orphan-task", }); assert.deepEqual(result, { taskId, status: "cancelled" }); - const [stop, ...rest] = yield* Ref.get(dispatched); - assert.isEmpty(rest); - assert.equal(stop?.type, "subagent.stop"); + const commands = yield* Ref.get(dispatched); + // A child whose run finished can still run another turn's work, so it is stopped too. + assert.deepEqual( + commands.flatMap((command) => + command.type === "thread.stop" ? [command.threadId] : [], + ), + childState === "a finished run" ? [childThreadId] : [], + ); + const stop = commands.find((command) => command.type === "subagent.stop"); if (stop?.type !== "subagent.stop") return; assert.deepEqual( { threadId: stop.threadId, runId: stop.runId, subagentId: stop.subagentId }, diff --git a/apps/server/src/mcp/OrchestratorMcpService.ts b/apps/server/src/mcp/OrchestratorMcpService.ts index 7d88e4d5f759b..f1ac8c5703815 100644 --- a/apps/server/src/mcp/OrchestratorMcpService.ts +++ b/apps/server/src/mcp/OrchestratorMcpService.ts @@ -2359,8 +2359,7 @@ const make = Effect.gen(function* () { ), ); // A follow-up that lost its child run (#19002) stays running on the parent, while its - // status here comes from the child's last run. The parent cancels it, and any other - // orphan of the same child, when that child is idle. + // status here comes from the child's last run. Stop on the parent cancels that task. const cancelOrphan = Effect.gen(function* () { if (parentTask?.runId == null || !isOrchestrationV2WorkActive(parentTask.status)) return false; @@ -2383,12 +2382,13 @@ const make = Effect.gen(function* () { return subagents.some((task) => task.id === input.taskId && task.status === "cancelled"); }); if (isTerminalTaskStatus(current.status)) { - if (yield* cancelOrphan) return { taskId: input.taskId, status: "cancelled" }; + // The child can still run work for another turn, so it is stopped either way. yield* stopWithinLimit; + const cancelled = yield* cancelOrphan; yield* disposeCompletionDelivery; return { taskId: input.taskId, - status: current.status, + status: cancelled ? "cancelled" : current.status, } satisfies OrchestratorMcpTaskCancelResult; } const child = yield* loadProjection(current.childThreadId); diff --git a/apps/server/src/orchestration-v2/Orchestrator.ts b/apps/server/src/orchestration-v2/Orchestrator.ts index ea677c39dcf92..f969b148fc485 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.ts @@ -563,6 +563,20 @@ function withoutPreparationResult( return rest; } +/** Stop ended this task, so its result is not delivered to the parent agent. */ +function cancelledAppOwnedTask( + task: OrchestrationV2Subagent, + now: DateTime.Utc, +): OrchestrationV2Subagent { + return { + ...task, + status: "cancelled", + completedAt: now, + updatedAt: now, + completionDelivery: { state: "disposed", observedByRunId: null }, + }; +} + function isBlockingRun(run: OrchestrationV2Run): boolean { return ( run.status === "preparing" || @@ -9421,22 +9435,46 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio ]); const run = projection.runs.find((candidate) => candidate.id === command.runId); const subagent = projection.subagents.find((candidate) => candidate.id === command.subagentId); - // An app-owned task stops through its child thread. Here the parent only cancels the child's - // orphaned tasks: the child is idle and no child run is bound to them (#19002). + // A user Stop cancels an app-owned task here when nothing else can settle it: no child run was + // ever bound to it and it is not the child's original task (#19002). This holds whatever else + // the child is doing. A task with a bound run, live or ended, keeps its settlement path, since + // its result may still be on the way, and the child stop the client sends next interrupts the + // run. A task that already ended is written again, so a client holding a stale copy, such as + // one cached before a repair, shows it ended. if ( subagent?.origin === "app_owned" && subagent.childThreadId !== null && subagent.runId === command.runId ) { - const records = yield* loadProjectionForCommand(command, ["subagents", "turnItems"], { + const childThreadId = subagent.childThreadId; + const child = yield* projectionStore.getThreadRecords(childThreadId, ["runs"]).pipe( + Effect.catchTags({ ProjectionStoreThreadNotFoundError: () => Effect.succeed(null) }), + Effect.mapError( + (cause) => new OrchestratorProjectionError({ threadId: childThreadId, cause }), + ), + ); + const active = isOrchestrationV2WorkActive(subagent.status); + if ( + active && + child !== null && + ((child.thread.forkedFrom?.type === "node" && + child.thread.forkedFrom.nodeId === subagent.id) || + child.runs.some((run) => run.delegatedTaskId === subagent.id)) + ) + return; + const records = yield* loadProjectionForCommand(command, ["nodes", "turnItems"], { turnItemTypes: ["subagent"], - turnItemStatuses: ["pending", "running", "waiting"], + turnItemRunIds: [subagent.runId], + omitSettledLeafNodes: true, }); - return yield* cancelIdleOrphans({ + const now = yield* DateTime.now; + return yield* writeAppOwnedTask({ command, events, - projection: records, - childThreadId: subagent.childThreadId, + task: active ? cancelledAppOwnedTask(subagent, now) : subagent, + nodes: records.nodes, + turnItems: records.turnItems, + now, }); } if ( @@ -9878,16 +9916,56 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio return undefined; }); + /** Writes a parent's app-owned task, and sets its node and turn items to the task's status. */ + const writeAppOwnedTask = Effect.fn("Orchestrator.writeAppOwnedTask")(function* (input: { + readonly command: Extract< + OrchestrationV2ServerCommand, + { readonly type: "run.interrupt" | "thread.stop" | "subagent.stop" } + >; + readonly events: Ref.Ref>; + readonly task: OrchestrationV2Subagent; + readonly nodes: ReadonlyArray; + readonly turnItems: ReadonlyArray; + readonly now: DateTime.Utc; + }) { + const { command, task, now } = input; + const emitEvent = emit(input.events, command); + yield* emitEvent({ + type: "subagent.updated", + threadId: command.threadId, + nodeId: task.id, + occurredAt: now, + payload: task, + }); + const node = input.nodes.find((candidate) => candidate.id === task.id); + if (node !== undefined) + yield* emitEvent({ + type: "node.updated", + threadId: command.threadId, + nodeId: node.id, + occurredAt: now, + payload: { ...node, status: task.status, completedAt: task.completedAt }, + }); + for (const item of input.turnItems) { + if (item.type !== "subagent" || item.subagentId !== task.id) continue; + yield* emitEvent({ + type: "turn-item.updated", + threadId: command.threadId, + nodeId: task.id, + occurredAt: now, + payload: { ...item, status: task.status, completedAt: task.completedAt, updatedAt: now }, + }); + } + }); + const cancelIdleOrphans = Effect.fn("Orchestrator.cancelIdleOrphans")( function* (input: { readonly command: Extract< OrchestrationV2ServerCommand, - { readonly type: "run.interrupt" | "thread.stop" | "subagent.stop" } + { readonly type: "run.interrupt" | "thread.stop" } >; readonly events: Ref.Ref>; readonly projection: Pick; - /** Limits the repair to tasks delegated to this child thread. */ - readonly childThreadId?: ThreadId; }) { const { command, events, projection } = input; const pending = yield* Ref.get(events); @@ -9905,8 +9983,7 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio if ( task.origin !== "app_owned" || task.childThreadId === null || - !isOrchestrationV2WorkActive(task.status) || - (input.childThreadId !== undefined && task.childThreadId !== input.childThreadId) + !isOrchestrationV2WorkActive(task.status) ) continue; const childThreadId = task.childThreadId; @@ -9953,40 +10030,15 @@ const makeOrchestrator = Effect.fn("orchestrationV2.Orchestrator.layer")(functio if (!idle) continue; // Stop can cancel an idle orphan without guessing which completed run owned its result. const now = yield* DateTime.now; - const emitEvent = emit(events, command); - yield* emitEvent({ - type: "subagent.updated", - threadId: command.threadId, - nodeId: task.id, - occurredAt: now, - payload: { - ...task, - status: "cancelled", - completedAt: now, - updatedAt: now, - completionDelivery: { state: "disposed", observedByRunId: null }, - }, - }); nodes ??= (yield* projectionStore.getThreadRecords(command.threadId, ["nodes"])).nodes; - const node = nodes.find((candidate) => candidate.id === task.id); - if (node !== undefined) - yield* emitEvent({ - type: "node.updated", - threadId: command.threadId, - nodeId: node.id, - occurredAt: now, - payload: { ...node, status: "cancelled", completedAt: now }, - }); - for (const item of projection.turnItems) { - if (item.type !== "subagent" || item.subagentId !== task.id) continue; - yield* emitEvent({ - type: "turn-item.updated", - threadId: command.threadId, - nodeId: task.id, - occurredAt: now, - payload: { ...item, status: "cancelled", completedAt: now, updatedAt: now }, - }); - } + yield* writeAppOwnedTask({ + command, + events, + task: cancelledAppOwnedTask(task, now), + nodes, + turnItems: projection.turnItems, + now, + }); } }, (effect, input) => diff --git a/apps/server/src/orchestration-v2/ThreadStop.test.ts b/apps/server/src/orchestration-v2/ThreadStop.test.ts index e041b5628bb2a..c8b490008763a 100644 --- a/apps/server/src/orchestration-v2/ThreadStop.test.ts +++ b/apps/server/src/orchestration-v2/ThreadStop.test.ts @@ -166,6 +166,8 @@ it.effect.each([ ["idle", "run.interrupt"], ["idle", "subagent.stop"], ["running", "subagent.stop"], + ["bound", "subagent.stop"], + ["correlated", "subagent.stop"], ["archived", "thread.stop"], ["missing", "thread.stop"], ["running", "thread.stop"], @@ -178,7 +180,7 @@ it.effect.each([ ["question", "thread.stop"], ["restart", "thread.stop"], ] as const)( - "Stop clears an orphaned follow-up only when its child is finished (%s via %s)", + "Stop clears an orphaned follow-up when its child is finished; its own Stop does unless a run is bound (%s via %s)", ([childState, type]) => Effect.gen(function* () { const orchestrator = yield* Orchestrator.OrchestratorV2; @@ -279,7 +281,9 @@ it.effect.each([ const childRunStatus = childState === "running" || childState === "queued" || childState === "waiting" ? childState - : "completed"; + : childState === "bound" + ? "running" + : "completed"; yield* apply({ type: "run.updated", threadId: childThreadId, @@ -299,12 +303,14 @@ it.effect.each([ ...runAtStart, status: childRunStatus, completedAt: childRunStatus === "completed" ? now : null, - ...(childState === "correlated" ? { delegatedTaskId: taskId } : {}), + ...(childState === "correlated" || childState === "bound" + ? { delegatedTaskId: taskId } + : {}), }, }); assert.equal( (yield* orchestrator.getThreadProjection(childThreadId)).runs[0]?.delegatedTaskId, - childState === "correlated" ? taskId : undefined, + childState === "correlated" || childState === "bound" ? taskId : undefined, ); if (childState === "provider-active" || childState === "roster") { yield* apply({ @@ -438,7 +444,13 @@ it.effect.each([ (after.thread.pullRequests ?? []).filter((link) => link.watch !== undefined), type === "subagent.stop" ? 1 : 0, ); - const expected = childState === "idle" || childState === "archived" ? "cancelled" : "running"; + // A task with a bound run, live or ended, keeps its settlement path. + const expected = + childState === "bound" || childState === "correlated" + ? "running" + : type === "subagent.stop" || childState === "idle" || childState === "archived" + ? "cancelled" + : "running"; assert.equal(after.subagents.find((task) => task.id === taskId)?.status, expected); assert.equal(after.nodes.find((node) => node.id === taskId)?.status, expected); assert.equal(after.turnItems.find((item) => item.id === itemId)?.status, expected); @@ -468,6 +480,28 @@ it.effect.each([ assert.equal(repeated.subagents.find((task) => task.id === taskId)?.status, "cancelled"); assert.deepEqual(repeated.messages, after.messages); assert.deepEqual(repeated.contextTransfers, after.contextTransfers); + if (type === "subagent.stop") { + // A client that missed the cancel, such as one resumed from an older cache, still + // shows the task as running. Its Stop gets the ended task back. + const eventStore = yield* EventStore.EventStoreV2; + const afterSequence = yield* eventStore.latestSequence({ threadId: parentThreadId }); + yield* orchestrator.dispatch({ + ...stop, + commandId: CommandId.make(`stop-orphan-stale-client:${childState}`), + type, + runId: original.runId!, + subagentId: taskId, + }); + const resent = yield* eventStore + .read({ threadId: parentThreadId, afterSequence }) + .pipe(Stream.runCollect); + assert.deepEqual( + resent.flatMap(({ event }) => + event.type === "subagent.updated" ? [event.payload.status] : [], + ), + ["cancelled"], + ); + } } }).pipe(Effect.provide(layerTest)), ); diff --git a/apps/web/src/components/ThreadContextChip.tsx b/apps/web/src/components/ThreadContextChip.tsx index f120cbde298e2..5f1a4202f1d36 100644 --- a/apps/web/src/components/ThreadContextChip.tsx +++ b/apps/web/src/components/ThreadContextChip.tsx @@ -1,9 +1,11 @@ +import { scopeProjectRef } from "@t3tools/client-runtime/environment"; import type { ThreadContextRecord } from "@t3tools/contracts"; import { Link } from "@tanstack/react-router"; import { MessagesSquareIcon } from "lucide-react"; -import { useThreadShell } from "~/state/entities"; +import { useProject, useThreadShell } from "~/state/entities"; import { ContextChip, ContextChipLabel } from "./ContextChip"; +import { ProjectFavicon } from "./ProjectFavicon"; import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; export function ThreadContextChip(props: { @@ -13,6 +15,7 @@ export function ThreadContextChip(props: { }) { const { environmentId, threadId } = props.record; const shell = useThreadShell({ environmentId, threadId }); + const project = useProject(shell ? scopeProjectRef(environmentId, shell.projectId) : null); const title = shell?.title?.trim() || props.record.title; return ( @@ -26,7 +29,11 @@ export function ThreadContextChip(props: { data-markdown-copy={props.copyMarkdown} className="no-underline" > - + {project ? ( + + ) : ( + + )} {title} } diff --git a/apps/web/src/components/chat/MarkdownThreadLink.tsx b/apps/web/src/components/chat/MarkdownThreadLink.tsx index 4996be3884a57..8b32a541223d2 100644 --- a/apps/web/src/components/chat/MarkdownThreadLink.tsx +++ b/apps/web/src/components/chat/MarkdownThreadLink.tsx @@ -1,15 +1,13 @@ -import { scopeProjectRef, scopeThreadRef } from "@t3tools/client-runtime/environment"; +import { scopeThreadRef } from "@t3tools/client-runtime/environment"; import type { EnvironmentId, ThreadId } from "@t3tools/contracts"; import { formatThreadLink, percentDecodedThreadLinkId, resolveThreadLinkRef, } from "@t3tools/shared/threadLinks"; -import { Link } from "@tanstack/react-router"; -import { MessageSquareTextIcon } from "lucide-react"; -import { useProject, useThreadRefs, useThreadShell } from "../../state/entities"; -import { ProjectFavicon } from "../ProjectFavicon"; +import { useThreadRefs, useThreadShell } from "../../state/entities"; +import { ThreadContextChip } from "../ThreadContextChip"; export function MarkdownThreadLink(props: { readonly environmentId: EnvironmentId; @@ -31,28 +29,11 @@ export function MarkdownThreadLink(props: { const thread = local ?? remote; const environmentId = thread?.environmentId ?? props.environmentId; const threadId = thread?.id ?? props.threadId; - const project = useProject( - thread === null ? null : scopeProjectRef(environmentId, thread.projectId), - ); const title = thread?.title.trim() || props.label; return ( - - - {project === null ? ( - - ) : ( - - )} - - {title} - + ); } diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 346d4d85cde9c..354a096d6b237 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -1955,13 +1955,23 @@ code { text-decoration: none; } -/* File links are Chips and own their color and focus ring. */ -.chat-markdown a:not(.chat-markdown-file-link) { +/* File links and context chips own their color and focus ring. */ +.chat-markdown a:not(.chat-markdown-file-link, :has([data-slot="context-chip-label"])) { color: var(--info-foreground); } -.chat-markdown a:not(.chat-markdown-file-link, [data-markdown-image-link]):hover, -.chat-markdown a:not(.chat-markdown-file-link, [data-markdown-image-link]):focus-visible { +.chat-markdown + a:not( + .chat-markdown-file-link, + :has([data-slot="context-chip-label"]), + [data-markdown-image-link] + ):hover, +.chat-markdown + a:not( + .chat-markdown-file-link, + :has([data-slot="context-chip-label"]), + [data-markdown-image-link] + ):focus-visible { background-image: radial-gradient(circle, currentcolor 0.75px, transparent 1px); background-position: left bottom; background-repeat: repeat-x; diff --git a/apps/web/src/reactCompilerCoverage.test.ts b/apps/web/src/reactCompilerCoverage.test.ts index 1d43c9e38b012..f0e479d271023 100644 --- a/apps/web/src/reactCompilerCoverage.test.ts +++ b/apps/web/src/reactCompilerCoverage.test.ts @@ -18,16 +18,24 @@ const webSourceFiles = NodeFS.readdirSync(NodePath.join(repoRoot, "apps/web/src" .filter((file) => !file.endsWith(".gen.ts")) .map((file) => `apps/web/src/${file}`); +// ChatView is ~9k lines. Compiling it takes ~9s on a fast machine and passes the +// 15s project timeout on loaded CI runners. +const slowFiles = new Set(["apps/web/src/components/ChatView.tsx"]); + +const compile = async (file: string) => { + const absolute = NodePath.join(repoRoot, file); + const result = await transform(absolute, NodeFS.readFileSync(absolute, "utf8"), { + jsx: { runtime: "automatic", development: false, refresh: false }, + reactCompiler: { reportDiagnostics: true }, + }); + const bailouts = result.errors.map((error) => error.codeframe ?? error.message); + expect(bailouts).toEqual([]); +}; + // Lint reports most compiler bailouts, but not every one (try/finally, throw // inside try). A skipped file still builds, just unmemoized, so check the output. describe("React Compiler coverage", () => { - it.each(webSourceFiles)("compiles %s", async (file) => { - const absolute = NodePath.join(repoRoot, file); - const result = await transform(absolute, NodeFS.readFileSync(absolute, "utf8"), { - jsx: { runtime: "automatic", development: false, refresh: false }, - reactCompiler: { reportDiagnostics: true }, - }); - const bailouts = result.errors.map((error) => error.codeframe ?? error.message); - expect(bailouts).toEqual([]); - }); + it.each(webSourceFiles.filter((file) => !slowFiles.has(file)))("compiles %s", compile); + // oxlint-disable-next-line t3code/no-test-timeout-or-unreasoned-skip -- CPU-bound compile of a huge file, not a hidden wait. + it.each([...slowFiles])("compiles %s", compile, 60_000); }); diff --git a/packages/client-runtime/src/operations/commands.ts b/packages/client-runtime/src/operations/commands.ts index edb9d70ef6fa9..e28df43e6a2d2 100644 --- a/packages/client-runtime/src/operations/commands.ts +++ b/packages/client-runtime/src/operations/commands.ts @@ -774,7 +774,7 @@ export const interruptThreadTurn = Effect.fn("EnvironmentCommands.interruptThrea const projection = yield* getProjection(input.threadId); const subagent = projection.subagents.find((agent) => agent.id === input.subagentId); if (subagent?.origin === "app_owned" && subagent.childThreadId !== null) { - // The parent cancels the task if it is an orphan with an idle child (#19002). Older + // The parent ends the task even when its child lost track of it (#19002). Older // servers reject this for app-owned tasks, so the child stop below still runs. if (subagent.runId !== null) { yield* dispatch({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90910fe480cd0..63794e7f7adb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -229,7 +229,7 @@ importers: dependencies: '@clerk/electron': specifier: 0.1.1 - version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.3)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@clerk/electron-passkeys': specifier: 0.0.3 version: 0.0.3 @@ -264,8 +264,8 @@ importers: specifier: 4.0.5 version: 4.0.5 electron: - specifier: 44.4.5 - version: 44.4.5 + specifier: 44.9.3 + version: 44.9.3 electron-store: specifier: ^8.2.0 version: 8.2.0 @@ -824,7 +824,7 @@ importers: version: 6.39.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@clerk/electron': specifier: 0.1.1 - version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + version: 0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.3)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@clerk/react': specifier: 6.17.8 version: 6.17.8(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -8437,8 +8437,8 @@ packages: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@44.4.5: - resolution: {integrity: sha512-SjgoaeYsSWZfJzubgQU7juvuXMTvn6/e1gAHdGFA/yuMbpF+I+skYqIJ6DdXBXHeWbkbpNpmwZCTpiivtlWZSw==} + electron@44.9.3: + resolution: {integrity: sha512-Cv9/yMnZxNWsMlIe8T4v//NKHdUvbeyoTJoXE3BIcRxus2meeFaO9h/P3zB5d4cBXum51IgAYrT2QjpIDWKpJA==} engines: {node: '>= 22.12.0'} hasBin: true @@ -13529,12 +13529,12 @@ snapshots: '@clerk/electron-passkeys-win32-arm64-msvc': 0.0.3 '@clerk/electron-passkeys-win32-x64-msvc': 0.0.3 - '@clerk/electron@0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.5)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + '@clerk/electron@0.1.1(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.9.3)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: '@clerk/clerk-js': 6.39.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@clerk/react': 6.17.8(react-dom@19.3.0(react@19.3.0))(react@19.3.0) '@clerk/shared': 4.40.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - electron: 44.4.5 + electron: 44.9.3 react: 19.3.0 tslib: 2.8.1 optionalDependencies: @@ -18711,7 +18711,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron@44.4.5: + electron@44.9.3: dependencies: '@electron-internal/extract-zip': 1.0.5 '@electron/get': 5.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0f93a112fb9ab..2def61a3f8a35 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -153,7 +153,7 @@ minimumReleaseAgeExclude: - expo-sharing@58.0.19 - expo-sqlite@58.0.13 - expo-symbols@58.0.4 - - electron@44.4.5 + - electron@44.9.3 - "@anthropic-ai/claude-agent-sdk@0.3.276" - "@alchemy.run/cloudflare-runtime@2.0.0-beta.80" - "@alchemy.run/floci@2.0.0-beta.80"