From 6337264968c6513b8f90a9376c6f314037274af3 Mon Sep 17 00:00:00 2001 From: Igor Makowski <56691628+Mnigos@users.noreply.github.com> Date: Tue, 22 Sep 2026 00:33:44 +0200 Subject: [PATCH 001/104] fix(contracts): old message-sent events without turnId no longer stop the server from starting (#12763) (cherry picked from commit 80d9c181da41ee3890791c9b65018ce093e7bc9e) --- packages/contracts/src/orchestration.test.ts | 31 ++++++++++++++++++++ packages/contracts/src/orchestration.ts | 3 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/contracts/src/orchestration.test.ts b/packages/contracts/src/orchestration.test.ts index 5e9a330a68..c4ba1a5b92 100644 --- a/packages/contracts/src/orchestration.test.ts +++ b/packages/contracts/src/orchestration.test.ts @@ -872,6 +872,37 @@ it.effect("decodes thread pull request links with snapshot and stack", () => }), ); +// A stored event that fails to decode stops the event store read, and with it +// server startup, so rows written before `turnId` existed must still load. +it.effect("decodes a legacy message-sent event persisted without turnId", () => + Effect.gen(function* () { + const event = yield* decodeOrchestrationEvent({ + sequence: 539, + eventId: "event-message-legacy-1", + aggregateKind: "thread", + aggregateId: "thread-1", + type: "thread.message-sent", + occurredAt: "2026-01-01T00:00:00.000Z", + commandId: "cmd-message-legacy-1", + causationEventId: null, + correlationId: "cmd-message-legacy-1", + metadata: {}, + payload: { + threadId: "thread-1", + messageId: "message-1", + role: "user", + text: "written before turn ids were recorded", + streaming: false, + createdAt: "2026-01-01T00:00:00.000Z", + updatedAt: "2026-01-01T00:00:00.000Z", + }, + }); + assert.strictEqual(event.type, "thread.message-sent"); + if (event.type !== "thread.message-sent") return; + assert.strictEqual(event.payload.turnId, null); + }), +); + it.effect("decodes thread archived and unarchived events", () => Effect.gen(function* () { const archived = yield* decodeOrchestrationEvent({ diff --git a/packages/contracts/src/orchestration.ts b/packages/contracts/src/orchestration.ts index 20ff687714..bed46a0314 100644 --- a/packages/contracts/src/orchestration.ts +++ b/packages/contracts/src/orchestration.ts @@ -2211,7 +2211,8 @@ export const ThreadMessageSentPayload = Schema.Struct({ text: Schema.String, attachments: Schema.optional(Schema.Array(ChatAttachment)), context: Schema.optional(OrchestrationMessageContext), - turnId: Schema.NullOr(TurnId), + // Events persisted before the field existed carry no key at all. + turnId: Schema.NullOr(TurnId).pipe(Schema.withDecodingDefault(Effect.succeed(null))), streaming: Schema.Boolean, createdAt: IsoDateTime, updatedAt: IsoDateTime, From 812934597edf058dad066e3d87cf6a1b1ae2a247 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sun, 20 Sep 2026 22:08:51 -0700 Subject: [PATCH 002/104] fix(devices): safely reclaim obsolete managed tool versions (#12819) (cherry picked from commit 790be6d755026d61bd13a970dd77176c12ca0425) --- apps/server/src/device/LocalDeviceHost.ts | 11 ++ .../src/device/deviceToolMaintenance.test.ts | 159 ++++++++++++++++++ .../src/device/deviceToolMaintenance.ts | 148 ++++++++++++++++ .../server/src/device/sshDeviceScript.test.ts | 3 + apps/server/src/device/sshDeviceScript.ts | 3 + 5 files changed, 324 insertions(+) create mode 100644 apps/server/src/device/deviceToolMaintenance.test.ts create mode 100644 apps/server/src/device/deviceToolMaintenance.ts diff --git a/apps/server/src/device/LocalDeviceHost.ts b/apps/server/src/device/LocalDeviceHost.ts index 28143f5b8e..2db9c375f5 100644 --- a/apps/server/src/device/LocalDeviceHost.ts +++ b/apps/server/src/device/LocalDeviceHost.ts @@ -1,3 +1,4 @@ +import { pruneLocalDeviceTools } from "./deviceToolMaintenance.ts"; import { deviceToolInstallMessage } from "@t3tools/contracts"; /** * The device host that is this machine. @@ -582,6 +583,11 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { return yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () { const hub = yield* restore(spawnHub(hubTool)); + yield* pruneLocalDeviceTools(config.baseDir, process.execPath, "hub").pipe( + Effect.provideService(Path.Path, path), + Effect.provideService(ProcessRunner.ProcessRunner, runner), + Effect.ignore, + ); const candidate = helperPaths(hubTool); const [axExists, cliExists] = yield* restore( Effect.all([ @@ -643,6 +649,11 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { agentToolRef = agentTool; yield* onPhase("starting"); const agentDevice = yield* startAgentDeviceDaemon(agentTool); + yield* pruneLocalDeviceTools(config.baseDir, process.execPath, "agent").pipe( + Effect.provideService(Path.Path, path), + Effect.provideService(ProcessRunner.ProcessRunner, runner), + Effect.ignore, + ); const next = { ...running, agentDevice }; yield* Ref.set(runningRef, next); return { ...toReady(next), agentDevice }; diff --git a/apps/server/src/device/deviceToolMaintenance.test.ts b/apps/server/src/device/deviceToolMaintenance.test.ts new file mode 100644 index 0000000000..4c46be11bb --- /dev/null +++ b/apps/server/src/device/deviceToolMaintenance.test.ts @@ -0,0 +1,159 @@ +import * as Effect from "effect/Effect"; +import * as NodePathLayer from "@effect/platform-node/NodePath"; +import * as ProcessRunner from "../processRunner.ts"; +import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; +// @effect-diagnostics nodeBuiltinImport:off - tests the same standalone script used by local and SSH hosts. +import { describe, expect, it } from "@effect/vitest"; +import * as NodeFSP from "node:fs/promises"; +import * as NodeOS from "node:os"; +import * as NodePath from "node:path"; +import * as NodeChildProcess from "node:child_process"; +import * as NodeUtil from "node:util"; +import { pruneLocalDeviceTools, deviceToolMaintenanceScript } from "./deviceToolMaintenance.ts"; + +const exec = NodeUtil.promisify(NodeChildProcess.execFile); + +describe.each([false, true])("device tool cleanup, flat=%s", (flat) => { + it("keeps current, previous, active and incomplete installs, pruning unused completed versions", async () => { + const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); + const name = "expo-device-hub"; + const directory = (version: string) => + flat ? NodePath.join(root, `${name}@${version}`) : NodePath.join(root, name, version); + try { + for (const version of ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0"]) { + await NodeFSP.mkdir(directory(version), { recursive: true }); + if (version === "0.5.0") continue; + const sentinel = NodePath.join(directory(version), ".install-complete"); + await NodeFSP.writeFile(sentinel, version); + await NodeFSP.utimes( + sentinel, + Number(version.split(".")[1]), + Number(version.split(".")[1]), + ); + } + const script = + deviceToolMaintenanceScript + + ` +(async () => { + const root = ${JSON.stringify(root)}; + await pruneTools(root, [['${name}', '0.6.0']], ${flat}); +})().catch(error => { console.error(error); process.exitCode = 1; });`; + await exec(process.execPath, [ + "-e", + script, + NodePath.join(directory("0.2.0"), "active-helper.cjs"), + ]); + await expect(NodeFSP.stat(directory("0.1.0"))).rejects.toThrow(); + await expect(NodeFSP.stat(directory("0.3.0"))).rejects.toThrow(); + for (const version of ["0.2.0", "0.4.0", "0.5.0", "0.6.0"]) + expect((await NodeFSP.stat(directory(version))).isDirectory()).toBe(true); + } finally { + await NodeFSP.rm(root, { recursive: true, force: true }); + } + }); + + it("keeps every install when the process scan fails", async () => { + const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-scan-")); + try { + for (const version of ["0.1.0", "0.2.0", "0.3.0"]) { + const dir = flat + ? NodePath.join(root, `expo-device-hub@${version}`) + : NodePath.join(root, "expo-device-hub", version); + await NodeFSP.mkdir(dir, { recursive: true }); + await NodeFSP.writeFile(NodePath.join(dir, ".install-complete"), version); + } + await exec(process.execPath, [ + "-e", + deviceToolMaintenanceScript + + ` + require('node:child_process').spawnSync = () => ({ status: 1, stdout: '' }); + pruneTools(${JSON.stringify(root)}, [['expo-device-hub', '0.3.0']], ${flat}).catch(() => process.exitCode = 1); + `, + ]); + const parent = flat ? root : NodePath.join(root, "expo-device-hub"); + expect((await NodeFSP.readdir(parent)).length).toBe(3); + } finally { + await NodeFSP.rm(root, { recursive: true, force: true }); + } + }); + + it("does not prune before the required version has completed installation", async () => { + const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); + try { + const dir = flat + ? NodePath.join(root, "expo-device-hub@0.1.0") + : NodePath.join(root, "expo-device-hub/0.1.0"); + await NodeFSP.mkdir(dir, { recursive: true }); + await NodeFSP.writeFile(NodePath.join(dir, ".install-complete"), "0.1.0"); + await exec(process.execPath, [ + "-e", + deviceToolMaintenanceScript + + `pruneTools(${JSON.stringify(root)}, [['expo-device-hub','0.6.0']], ${flat}).catch(() => process.exitCode = 1);`, + ]); + expect((await NodeFSP.stat(dir)).isDirectory()).toBe(true); + } finally { + await NodeFSP.rm(root, { recursive: true, force: true }); + } + }); +}); + +it.effect("maintenance failures retain safe context and the original process result", () => + Effect.gen(function* () { + const output = { + code: ChildProcessSpawner.ExitCode(1), + stdout: "", + stderr: "private child diagnostics", + timedOut: false, + stdoutTruncated: false, + stderrTruncated: false, + stdoutInvalidUtf8: false, + stderrInvalidUtf8: false, + }; + for (const [operation, run] of [["prune", pruneLocalDeviceTools]] as const) { + const error = yield* run("/tools", process.execPath, "hub").pipe( + Effect.provideService(ProcessRunner.ProcessRunner, { run: () => Effect.succeed(output) }), + Effect.flip, + ); + expect(error).toMatchObject({ + _tag: "DeviceToolMaintenanceError", + operation, + tool: "hub", + exitCode: 1, + cause: output, + }); + expect(error.message).toBe(`Device tool ${operation} failed for hub (exit code 1).`); + expect(error.message).not.toContain(output.stderr); + } + }).pipe(Effect.provide(NodePathLayer.layer)), +); + +it("serializes competing maintenance processes after reclaiming a stale lock", async () => { + const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-contention-")); + try { + const lock = NodePath.join(root, ".maintenance-lock"); + await NodeFSP.mkdir(lock); + await NodeFSP.writeFile( + NodePath.join(lock, "stale-owner.json"), + JSON.stringify({ pid: 2147483647, identity: "dead" }), + ); + const script = + deviceToolMaintenanceScript + + ` +(async () => { + const root = ${JSON.stringify(root)}; + const marker = maintenancePath.join(root, 'critical-section'); + for (let attempt = 0; attempt < 8; attempt++) await withToolMaintenance(root, () => { + maintenanceFs.writeFileSync(marker, String(process.pid), { flag: 'wx' }); + for (let check = 0; check < 100; check++) { + if (maintenanceFs.readFileSync(marker, 'utf8') !== String(process.pid)) throw Error('Overlapping maintenance'); + } + maintenanceFs.unlinkSync(marker); + }); +})().catch(error => { console.error(error); process.exitCode = 1; });`; + await Promise.all(Array.from({ length: 6 }, () => exec(process.execPath, ["-e", script]))); + await expect(NodeFSP.stat(lock)).rejects.toThrow(); + expect(await NodeFSP.readdir(root)).toEqual([]); + } finally { + await NodeFSP.rm(root, { recursive: true, force: true }); + } +}); diff --git a/apps/server/src/device/deviceToolMaintenance.ts b/apps/server/src/device/deviceToolMaintenance.ts new file mode 100644 index 0000000000..43cc64e8fa --- /dev/null +++ b/apps/server/src/device/deviceToolMaintenance.ts @@ -0,0 +1,148 @@ +// @effect-diagnostics preferSchemaOverJson:off - JSON string literals safely embed paths and arguments in generated JavaScript. +import * as Schema from "effect/Schema"; +import * as Effect from "effect/Effect"; +import * as Path from "effect/Path"; +import * as ProcessRunner from "../processRunner.ts"; +import { AGENT_DEVICE_VERSION, DEVICE_HUB_VERSION } from "./DeviceToolchain.ts"; + +/** Shared with the SSH bootstrap. Cleanup runs only after successful startup. */ +export const deviceToolMaintenanceScript = String.raw` +const maintenanceFs = require('node:fs'); +const maintenancePath = require('node:path'); +const maintenanceAlive = pid => { + try { process.kill(pid, 0); return true; } + catch (error) { return error.code !== 'ESRCH'; } +}; +async function withToolMaintenance(root, operation) { + maintenanceFs.mkdirSync(root, { recursive: true }); + const lock = maintenancePath.join(root, '.maintenance-lock'); + const nonce = require('node:crypto').randomUUID(); + const ownerFile = process.pid + '.' + nonce + '.json'; + const candidate = lock + '.' + nonce; + const holder = { pid: process.pid }; + const deadline = Date.now() + 30000; + const removeEmptyLock = () => { + try { maintenanceFs.rmdirSync(lock); } + catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST', 'EPERM'].includes(error.code)) throw error; } + }; + maintenanceFs.mkdirSync(candidate); + try { + maintenanceFs.writeFileSync(maintenancePath.join(candidate, ownerFile), JSON.stringify(holder)); + while (true) { + try { + // Publish a populated directory atomically; rename cannot replace another populated lock. + maintenanceFs.renameSync(candidate, lock); + break; + } + catch (error) { + if (!['EEXIST', 'ENOTEMPTY', 'EPERM', 'EACCES'].includes(error.code)) throw error; + let files = []; + try { files = maintenanceFs.readdirSync(lock); } catch (error) { if (error.code !== 'ENOENT') throw error; } + if (files.length === 1) { + const previousFile = maintenancePath.join(lock, files[0]); + let previous; + try { previous = JSON.parse(maintenanceFs.readFileSync(previousFile, 'utf8')); } catch {} + if (Number.isSafeInteger(previous?.pid) && previous.pid > 0 && !maintenanceAlive(previous.pid)) { + // The unique filename belongs only to that owner. Never unlink a replacement owner's file. + try { maintenanceFs.unlinkSync(previousFile); } catch (error) { if (error.code !== 'ENOENT') throw error; } + } + } + // A concurrent acquirer publishes its owner file with the directory, so this cannot remove it. + removeEmptyLock(); + if (Date.now() >= deadline) throw Error('Device tool maintenance is locked. Retry when the other operation finishes.'); + await new Promise(resolve => setTimeout(resolve, 50)); + } + } + try { return operation(); } + finally { + maintenanceFs.unlinkSync(maintenancePath.join(lock, ownerFile)); + removeEmptyLock(); + } + } finally { + maintenanceFs.rmSync(candidate, { recursive: true, force: true }); + } +} +function pruneTools(root, specs, flat) { + return withToolMaintenance(root, () => { + // Keep installs used by any running helper, including older T3 releases. + const scan = process.platform === 'win32' + ? require('node:child_process').spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', 'Get-CimInstance Win32_Process | Select-Object -ExpandProperty CommandLine'], { encoding: 'utf8', timeout: 10000 }) + : require('node:child_process').spawnSync('ps', ['-ax', '-o', 'command='], { encoding: 'utf8', timeout: 10000 }); + if (scan.status !== 0 || !scan.stdout) return; + for (const [name, required] of specs) { + const parent = flat ? root : maintenancePath.join(root, name); + let names; + try { names = maintenanceFs.readdirSync(parent); } catch { continue; } + const completed = []; + for (const item of names) { + const version = flat ? (item.startsWith(name + '@') ? item.slice(name.length + 1) : '') : item; + if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?$/.test(version)) continue; + const directory = maintenancePath.join(parent, item); + try { + if (!maintenanceFs.lstatSync(directory).isDirectory()) continue; + if (maintenanceFs.readFileSync(maintenancePath.join(directory, '.install-complete'), 'utf8').trim() !== version) continue; + completed.push({ version, directory, modified: maintenanceFs.statSync(maintenancePath.join(directory, '.install-complete')).mtimeMs }); + } catch {} + } + // Never prune until the required install has completed. Retain the last other successful install. + if (!completed.some(value => value.version === required)) continue; + const previous = completed.filter(value => value.version !== required).sort((a, b) => b.modified - a.modified || b.version.localeCompare(a.version, 'en', { numeric: true }))[0]?.version; + for (const { version, directory } of completed) { + if (version === required || version === previous || scan.stdout.includes(directory + maintenancePath.sep)) continue; + maintenanceFs.rmSync(directory, { recursive: true, force: true }); + } + } + }); +} +`; + +class DeviceToolMaintenanceError extends Schema.TaggedError()( + "DeviceToolMaintenanceError", + { + operation: Schema.Literal("prune"), + tool: Schema.Literals(["hub", "agent"]), + exitCode: Schema.NullOr(Schema.Int), + cause: Schema.Defect(), + }, +) { + override get message() { + return `Device tool ${this.operation} failed for ${this.tool} (exit code ${this.exitCode ?? "unknown"}).`; + } +} + +const runMaintenance = Effect.fn("DeviceToolchain.maintenance")(function* ( + nodePath: string, + script: string, + operation: "prune", + tool: "hub" | "agent", +) { + const runner = yield* ProcessRunner.ProcessRunner; + const result = yield* runner.run({ + command: nodePath, + args: [ + "-e", + deviceToolMaintenanceScript + + "\n" + + script + + ".catch(error => { console.error(error.message); process.exitCode = 1; });", + ], + }); + if (result.code !== 0) + return yield* Effect.fail( + new DeviceToolMaintenanceError({ operation, tool, exitCode: result.code, cause: result }), + ); +}); + +export const pruneLocalDeviceTools = Effect.fn("DeviceToolchain.prune")(function* ( + baseDir: string, + nodePath: string, + tool: "hub" | "agent", +) { + const path = yield* Path.Path; + yield* runMaintenance( + nodePath, + `pruneTools(${JSON.stringify(path.join(baseDir, "tools"))}, ${JSON.stringify(tool === "hub" ? [["expo-device-hub", DEVICE_HUB_VERSION]] : [["agent-device", AGENT_DEVICE_VERSION]])}, false)`, + "prune", + tool, + ); +}); diff --git a/apps/server/src/device/sshDeviceScript.test.ts b/apps/server/src/device/sshDeviceScript.test.ts index 89021d8c04..8d9bc77d89 100644 --- a/apps/server/src/device/sshDeviceScript.test.ts +++ b/apps/server/src/device/sshDeviceScript.test.ts @@ -125,6 +125,9 @@ else { const child=spawn(process.execPath,[path.join(path.dirname(process.argv[1 ); await NodeFSP.mkdir(NodePath.join(root, "hosts/one"), { recursive: true }); await NodeFSP.writeFile(NodePath.join(root, "hosts/one/fail-start-once"), ""); + // Unavailable advisory bookkeeping must not prevent either helper from starting. + await NodeFSP.writeFile(NodePath.join(root, "tools/.maintenance-lock"), "blocked"); + await NodeFSP.writeFile(NodePath.join(root, "tools/.users"), "unwritable lease directory"); try { const [manual, concurrent] = await Promise.all([ invoke("one", "start"), diff --git a/apps/server/src/device/sshDeviceScript.ts b/apps/server/src/device/sshDeviceScript.ts index 4c156870a4..de412b5873 100644 --- a/apps/server/src/device/sshDeviceScript.ts +++ b/apps/server/src/device/sshDeviceScript.ts @@ -1,3 +1,4 @@ +import { deviceToolMaintenanceScript } from "./deviceToolMaintenance.ts"; import { AGENT_DEVICE_VERSION, DEVICE_HUB_VERSION } from "./DeviceToolchain.ts"; export const quoteRemoteArg = (value: string) => `'${value.replaceAll("'", "'\"'\"'")}'`; @@ -28,6 +29,7 @@ const mode = ${JSON.stringify(mode)}; const hubVersion = ${JSON.stringify(DEVICE_HUB_VERSION)}; const agentVersion = ${JSON.stringify(AGENT_DEVICE_VERSION)}; ` + + deviceToolMaintenanceScript + String.raw` const fs = require('node:fs'); const path = require('node:path'); @@ -214,6 +216,7 @@ async function install(name, version, entry) { } const vendor = path.resolve(path.dirname(hubEntry), '../../vendor/serve-sim/dist'); const optional = file => fs.existsSync(file) ? file : null; + await pruneTools(path.join(root, 'tools'), [['expo-device-hub', hubVersion], ...(mode === 'agent-start' ? [['agent-device', agentVersion]] : [])], true).catch(() => {}); console.log(JSON.stringify({ nodePath: process.execPath, platforms, tools: versions(), hubPort: hub.port, ...agentResult, helpers: { serveSimAxSettings: optional(path.join(vendor, 'simax/serve-sim-ax-settings')), serveSimCli: optional(path.join(vendor, 'serve-sim.js')) } })); } finally { releaseHost(); } From de067a05eb789f1e384200691097e1919ccfef75 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 21 Sep 2026 00:14:19 -0700 Subject: [PATCH 003/104] feat(devices): offer manual updates in tool version details (#12877) (cherry picked from commit 1de563c1491c7d82563e4553bf5bf689ce6adbb9) --- apps/server/src/auth/RpcAuthorization.test.ts | 7 ++ apps/server/src/auth/RpcAuthorization.ts | 4 +- apps/server/src/device/DeviceService.test.ts | 80 +++++++++++++++++-- apps/server/src/device/DeviceService.ts | 34 +++++++- .../provider/Layers/ProviderService.test.ts | 1 + apps/server/src/ws.ts | 10 ++- .../components/device/DeviceToolVersions.tsx | 2 +- .../settings/IntegrationsSettings.tsx | 80 +++++++++++++++---- packages/contracts/src/device.ts | 3 + 9 files changed, 192 insertions(+), 29 deletions(-) diff --git a/apps/server/src/auth/RpcAuthorization.test.ts b/apps/server/src/auth/RpcAuthorization.test.ts index 000ca084d3..4fb0e26676 100644 --- a/apps/server/src/auth/RpcAuthorization.test.ts +++ b/apps/server/src/auth/RpcAuthorization.test.ts @@ -139,3 +139,10 @@ it("requires operate permission for host retry while preserving read-only listin AuthOrchestrationOperateScope, ); }); + +it("requires operate permission for tool updates even alongside a read-only check", () => { + expect(requiredScopeForDeviceList({ updateTool: "agent", inspectOnly: true })).toBe( + AuthOrchestrationOperateScope, + ); + expect(requiredScopeForDeviceList({ updateTool: "hub" })).toBe(AuthOrchestrationOperateScope); +}); diff --git a/apps/server/src/auth/RpcAuthorization.ts b/apps/server/src/auth/RpcAuthorization.ts index 31bc327584..6f522d156e 100644 --- a/apps/server/src/auth/RpcAuthorization.ts +++ b/apps/server/src/auth/RpcAuthorization.ts @@ -208,4 +208,6 @@ export function requiredScopeForRpcMethod(method: string): AuthEnvironmentScope /** Retrying can install or restart tools even though ordinary listing is readable. */ export const requiredScopeForDeviceList = (input: DeviceListInput): AuthEnvironmentScope => - input.retryHostId ? AuthOrchestrationOperateScope : AuthOrchestrationReadScope; + input.retryHostId || input.updateTool + ? AuthOrchestrationOperateScope + : AuthOrchestrationReadScope; diff --git a/apps/server/src/device/DeviceService.test.ts b/apps/server/src/device/DeviceService.test.ts index 8f104c8e8d..3bf3f6f7fc 100644 --- a/apps/server/src/device/DeviceService.test.ts +++ b/apps/server/src/device/DeviceService.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "@effect/vitest"; import { DEFAULT_SERVER_SETTINGS, DeviceId, + DeviceOperationError, EnvironmentId, LOCAL_DEVICE_HOST_ID, ProjectId, @@ -69,6 +70,7 @@ const fixture = Effect.fn("fixture")(function* ( failListAfterShutdown = false, runtimeFailure?: DeviceHost.DeviceHostError, inspectError = false, + installTool?: Parameters[3], ) { const settings = yield* Ref.make(DEFAULT_SERVER_SETTINGS); const starts: string[] = []; @@ -130,11 +132,15 @@ const fixture = Effect.fn("fixture")(function* ( starts.push("stop"); }), }; - const service = yield* makeWithHosts(new Map([[host.id, host]]), undefined, (hostId) => - Effect.sync(() => { - agentConfigs.push(hostId); - return "/test-agent-config.json"; - }), + const service = yield* makeWithHosts( + new Map([[host.id, host]]), + undefined, + (hostId) => + Effect.sync(() => { + agentConfigs.push(hostId); + return "/test-agent-config.json"; + }), + installTool, ).pipe( Effect.provideService(DeviceHost.DeviceHost, host), Effect.provideService( @@ -634,3 +640,67 @@ it.effect("failed read-only discovery preserves lifecycle status and installed i expect(starts).toEqual([]); }).pipe(Effect.scoped), ); + +it.effect( + "manual updates install only the selected tool without enabling access or starting helpers", + () => + Effect.gen(function* () { + const installed: string[] = []; + const { service, starts, agentStarts, requests } = yield* fixture( + Effect.void, + undefined, + false, + undefined, + false, + (tool) => + Effect.sync(() => { + installed.push(tool); + }), + ); + const before = yield* service.state; + const state = yield* service.updateTool("agent"); + expect(installed).toEqual(["agent"]); + expect(state.supportsToolUpdate).toBe(true); + expect(state.hostStatus).toBe(before.hostStatus); + expect(state.agentAccessEnabled).toBe(before.agentAccessEnabled); + expect(state.revision).toBeGreaterThan(before.revision); + expect(starts).toEqual([]); + expect(agentStarts).toEqual([]); + expect(requests).toEqual([]); + yield* service.updateTool("hub"); + expect(installed).toEqual(["agent", "hub"]); + }).pipe(Effect.scoped), +); + +it.effect("failed manual installation leaves lifecycle state unchanged and can be retried", () => + Effect.gen(function* () { + let attempts = 0; + const { service, starts, agentStarts } = yield* fixture( + Effect.void, + undefined, + false, + undefined, + false, + () => + Effect.suspend(() => + ++attempts === 1 + ? Effect.fail( + new DeviceOperationError({ + operation: "update device tool", + reason: "command_failed", + cause: new Error("offline"), + }), + ) + : Effect.void, + ), + ); + const before = yield* service.state; + const result = yield* service.updateTool("agent").pipe(Effect.result); + expect(result._tag).toBe("Failure"); + expect(yield* service.state).toEqual(before); + yield* service.updateTool("agent"); + expect(attempts).toBe(2); + expect(starts).toEqual([]); + expect(agentStarts).toEqual([]); + }).pipe(Effect.scoped), +); diff --git a/apps/server/src/device/DeviceService.ts b/apps/server/src/device/DeviceService.ts index 17d01de52b..eabd6e4929 100644 --- a/apps/server/src/device/DeviceService.ts +++ b/apps/server/src/device/DeviceService.ts @@ -37,7 +37,7 @@ import { } from "@t3tools/contracts"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; -import { ensureAgentDevice } from "./DeviceToolchain.ts"; +import { ensureAgentDevice, ensureDeviceHub } from "./DeviceToolchain.ts"; import * as ServerConfig from "../config.ts"; import { agentDeviceConfigPath, @@ -129,6 +129,7 @@ export class DeviceService extends Context.Service< ) => Effect.Effect; /** Refreshes devices only after device support has been enabled. */ readonly list: Effect.Effect; + readonly updateTool: (tool: "hub" | "agent") => Effect.Effect; readonly inspect: Effect.Effect; readonly retryHost: (hostId: DeviceHostId) => Effect.Effect; readonly open: (input: DeviceOpenInput) => Effect.Effect; @@ -185,6 +186,7 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* reason: "Agent configuration is unavailable in this device service.", }), ), + installTool?: (tool: "hub" | "agent") => Effect.Effect, ) { const settings = yield* ServerSettings.ServerSettingsService; const lifecycleLock = yield* Semaphore.make(1); @@ -208,6 +210,7 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* const stateRef = yield* SynchronizedRef.make({ state: { supportsHostRetry: true, + supportsToolUpdate: installTool !== undefined, supportsToolInspection: true, hosts: initialHosts, hostStatus: initialSettings.enabled ? "idle" : "disabled", @@ -909,6 +912,21 @@ export const makeWithHosts = Effect.fn("DeviceService.makeWithHosts")(function* return { ...DeviceService.of({ testHost, + updateTool: (tool) => + lifecycleLock.withPermit( + Effect.gen(function* () { + if (!installTool) + return yield* Effect.fail( + new DeviceOperationError({ + operation: "update device tool", + reason: "request_failed", + cause: new Error("Tool installation is unavailable in this device service."), + }), + ); + yield* installTool(tool); + return yield* inspect; + }), + ), retryHost, inspect, agentCli: Effect.fail( @@ -1047,6 +1065,20 @@ export const make = Effect.gen(function* () { ), ), configureAgent, + (tool) => + (tool === "hub" ? ensureDeviceHub(config.baseDir) : ensureAgentDevice(config.baseDir)).pipe( + Effect.provideService(FileSystem.FileSystem, fs), + Effect.provideService(Path.Path, path), + Effect.provideService(ProcessRunner.ProcessRunner, runner), + Effect.mapError( + (cause) => + new DeviceOperationError({ + operation: "update device tool", + reason: "command_failed", + cause, + }), + ), + ), ); const hostContext = yield* Effect.context>>(); diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index 1e228af47d..037de3436b 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -7165,6 +7165,7 @@ describe("agent browser access", () => { subscribe: unused(), inspect: unused(), retryHost: unused, + updateTool: unused, configure: unused, list: unused(), open: unused, diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index c25a4548fd..d030f9dab0 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -3731,13 +3731,15 @@ const makeWsRpcLayer = ( [WS_METHODS.deviceList]: (input) => observeRpcEffect( WS_METHODS.deviceList, - input.inspectOnly + input.inspectOnly && !input.updateTool ? deviceService.inspect : authorizeEffect( requiredScopeForDeviceList(input), - input.retryHostId - ? deviceService.retryHost(input.retryHostId) - : deviceService.list, + input.updateTool + ? deviceService.updateTool(input.updateTool) + : input.retryHostId + ? deviceService.retryHost(input.retryHostId) + : deviceService.list, ), { "rpc.aggregate": "device", diff --git a/apps/web/src/components/device/DeviceToolVersions.tsx b/apps/web/src/components/device/DeviceToolVersions.tsx index 0501c8370b..d9a79f4bdd 100644 --- a/apps/web/src/components/device/DeviceToolVersions.tsx +++ b/apps/web/src/components/device/DeviceToolVersions.tsx @@ -57,7 +57,7 @@ export function DeviceToolVersions({ .filter(([name]) => !kind || name === label) .map(([name, tool]) => (
-

{name}

+ {!kind ?

{name}

: null}
Running
{tool.runningVersion ?? "Not running"}
diff --git a/apps/web/src/components/settings/IntegrationsSettings.tsx b/apps/web/src/components/settings/IntegrationsSettings.tsx index 17aa65bf36..836cbb4f4b 100644 --- a/apps/web/src/components/settings/IntegrationsSettings.tsx +++ b/apps/web/src/components/settings/IntegrationsSettings.tsx @@ -646,7 +646,9 @@ function DeviceIntegrationControls({ const { state, loaded } = useDeviceState(environmentId); const configure = useAtomCommand(deviceEnvironment.configure); const list = useAtomCommand(deviceEnvironment.list, { reportFailure: false }); - const [pending, setPending] = useState<"hub" | "check" | "agent" | null>(null); + const [pending, setPending] = useState< + "hub" | "check" | "agent" | "update-hub" | "update-agent" | null + >(null); const busy = state.hostStatus === "installing" || state.hostStatus === "starting"; const [platformsRevealed, setPlatformsRevealed] = useState(false); // Keep diagnostics visible through subsequent agent setup and refresh phases. @@ -671,20 +673,64 @@ function DeviceIntegrationControls({ } }; - const checkVersions = state.supportsToolInspection ? ( - - ) : null; + const [updateError, setUpdateError] = useState<{ tool: "hub" | "agent"; message: string } | null>( + null, + ); + const localTools = state.hosts.find((host) => host.kind === "local")?.tools; + const versionActions = (tool: "hub" | "agent") => { + const version = localTools?.[tool]; + const needsUpdate = version && !version.installedVersions.includes(version.requiredVersion); + return ( +
+
+ {state.supportsToolUpdate && needsUpdate ? ( + + ) : null} + {state.supportsToolInspection ? ( + + ) : null} +
+ {updateError?.tool === tool ? ( +

+ {updateError.message} +

+ ) : null} +
+ ); + }; return ( <> @@ -694,7 +740,7 @@ function DeviceIntegrationControls({ control={ <> host.kind === "local")?.tools} /> @@ -751,7 +797,7 @@ function DeviceIntegrationControls({ control={ <> host.kind === "local")?.tools} /> diff --git a/packages/contracts/src/device.ts b/packages/contracts/src/device.ts index 31d42ef0a1..a57b2693ae 100644 --- a/packages/contracts/src/device.ts +++ b/packages/contracts/src/device.ts @@ -129,6 +129,7 @@ export type DeviceSession = typeof DeviceSession.Type; export const DeviceServiceState = Schema.Struct({ supportsHostRetry: Schema.optional(Schema.Boolean), + supportsToolUpdate: Schema.optional(Schema.Boolean), supportsToolInspection: Schema.optional(Schema.Boolean), hosts: Schema.Array(DeviceHostSummary), hostStatus: DeviceHostStatus, @@ -154,6 +155,8 @@ export const DeviceServiceState = Schema.Struct({ export type DeviceServiceState = typeof DeviceServiceState.Type; export const DeviceListInput = Schema.Struct({ + /** Install this server's pinned tool without enabling access or starting helpers. */ + updateTool: Schema.optional(Schema.Literals(["hub", "agent"])), /** Read inventory without installing tools or starting helpers. */ inspectOnly: Schema.optional(Schema.Boolean), /** Retry this host only, including agent tools if access was already granted. */ From f7d2a7e4a38f31e578a300d02a8178c5970975f9 Mon Sep 17 00:00:00 2001 From: "t3-code[bot]" <269035359+t3-code[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 10:58:10 -0700 Subject: [PATCH 004/104] feat(models): add opus 5.5 without changing existing aliases (#13094) Co-authored-by: Anco Co-authored-by: Exotic <118054752+extoci@users.noreply.github.com> Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> (cherry picked from commit f25a8e4b72c1164f2ab7b1c8481496f168d17f55) --- apps/server/src/provider/model-manifest.json | 3 ++- docs/internals/model-manifest.md | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/server/src/provider/model-manifest.json b/apps/server/src/provider/model-manifest.json index 47576b4796..268205645c 100644 --- a/apps/server/src/provider/model-manifest.json +++ b/apps/server/src/provider/model-manifest.json @@ -1,5 +1,6 @@ { "version": 1, + "updatedAt": "2026-09-22T17:51:55Z", "currentModels": { "codex": [ "gpt-6-astra", @@ -9,7 +10,7 @@ "gpt-daybreak-blue-latest", "gpt-daybreak-red-latest" ], - "claudeAgent": ["claude-fable-5-1", "claude-opus-5", "claude-sonnet-5"], + "claudeAgent": ["claude-fable-5-1", "claude-opus-5-5", "claude-opus-5", "claude-sonnet-5"], "antigravity": ["gemini-3.8-flash-high", "gemini-3.8-flash-medium", "gemini-3.8-flash-low"] }, "providers": { diff --git a/docs/internals/model-manifest.md b/docs/internals/model-manifest.md index ad31660ae5..c126485748 100644 --- a/docs/internals/model-manifest.md +++ b/docs/internals/model-manifest.md @@ -20,9 +20,11 @@ built-in catalog. Adding a model with an existing capability profile is a JSON edit; a new profile is needed only for a new capability combination. Codex still gets its model list from its app server. -`currentModels.claudeAgent` is frozen for releases that predate catalog discovery. -Do not extend it when adding Claude models. Codex uses `currentModels.codex` as a -legacy-classification overlay for discovered models. +`currentModels.claudeAgent` is the current-model classification overlay for +releases that predate catalog discovery; it does not add models to their catalogs. +Catalog-aware releases use `providers.claudeAgent.models[].status` instead. +Codex uses `currentModels.codex` as a legacy-classification overlay for discovered +models. Model data is schema-validated configuration. Tests should cover resolver, cache, and adapter semantics with synthetic model names, so adding a model never requires From ef05351508bc2f59d0104c4207af4af6556d395e Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Sun, 20 Sep 2026 23:24:41 -0700 Subject: [PATCH 005/104] fix(mobile): use a proper pull request icon on iOS (#12855) (cherry picked from commit 1eeeabb26f654c679efe265cd03edf69c3df3a6f) --- apps/mobile/src/components/AppSymbol.ios.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/apps/mobile/src/components/AppSymbol.ios.tsx b/apps/mobile/src/components/AppSymbol.ios.tsx index f1a28ed3f3..b5ac0e9b64 100644 --- a/apps/mobile/src/components/AppSymbol.ios.tsx +++ b/apps/mobile/src/components/AppSymbol.ios.tsx @@ -1,3 +1,4 @@ +import IconGitPullRequest from "@tabler/icons-react-native/IconGitPullRequest"; import { SymbolView as ExpoSymbolView, type SymbolViewProps } from "expo-symbols"; import { withUniwind } from "uniwind"; @@ -5,10 +6,24 @@ export type { SFSymbol } from "expo-symbols"; export type AppSymbolName = SymbolViewProps["name"]; /** - * Keep the iOS implementation isolated from the Android Tabler fallback so - * Metro does not initialize the icon package when iOS renders SF Symbols. + * Use SF Symbols on iOS except for pull requests, which have no matching + * native glyph. Import only that Tabler icon to keep the bundle small. */ function AppSymbolView(props: SymbolViewProps) { + const name = typeof props.name === "string" ? props.name : (props.name as { ios?: string })?.ios; + if (name === "arrow.triangle.pull") { + return ( + + ); + } + return ; } From 32bbc49aa783c8a94408c2d9d52088c90dc03d34 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 21 Sep 2026 14:49:08 -0700 Subject: [PATCH 006/104] fix(mobile): stop iOS autocorrect from rewriting search queries (#12949) Co-authored-by: Claude Fable 5 (cherry picked from commit 33cb911ceb89d55b812f486d4e001d0f86d77680) --- patches/react-native-screens@4.26.2.patch | 11 +- pnpm-lock.yaml | 2441 +++++++++------------ 2 files changed, 1055 insertions(+), 1397 deletions(-) diff --git a/patches/react-native-screens@4.26.2.patch b/patches/react-native-screens@4.26.2.patch index ee017ce199..9212cfc7b0 100644 --- a/patches/react-native-screens@4.26.2.patch +++ b/patches/react-native-screens@4.26.2.patch @@ -294,7 +294,7 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..a60b96b44e85d3f4b29dc0d190b95ab6 break; } case RNSScreenStackHeaderSubviewTypeCenter: -@@ -648,10 +693,470 @@ + (void)updateViewController:(UIViewController *)vc +@@ -648,10 +693,479 @@ + (void)updateViewController:(UIViewController *)vc // This assignment should be done after `navitem.titleView = ...` assignment (iOS 16.0 bug). // See: https://github.com/software-mansion/react-native-screens/issues/1570 (comments) navitem.title = config.title; @@ -586,6 +586,15 @@ index 1c844846a5c66e31cfa530ca462774d2fb6bbea1..a60b96b44e85d3f4b29dc0d190b95ab6 + ]]; + } + ++ // A bare UISearchTextField keeps UITextField defaults (sentence ++ // capitalization, autocorrect), unlike UISearchBar. Search queries are ++ // identifiers, so match the autoCapitalize "none" the JS fallbacks use. ++ if (resolvedSearchTextField != nil) { ++ resolvedSearchTextField.autocapitalizationType = UITextAutocapitalizationTypeNone; ++ resolvedSearchTextField.autocorrectionType = UITextAutocorrectionTypeNo; ++ resolvedSearchTextField.spellCheckingType = UITextSpellCheckingTypeNo; ++ } ++ + NSString *searchTextChangeId = mailSearchToolbarConfig[@"searchTextChangeId"]; + NSString *searchTextChangeActionIdentifier = @"org.react-native-screens.mail-search-toolbar.text-change"; + if (resolvedSearchTextField != nil) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fbdf753057..3bee54c251 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,7 +111,7 @@ patchedDependencies: react-native-keyboard-controller@1.21.13: 6e4339347bc5bb3c9ea67d85ff5c814058b211c5750f247aba59d07869a2e787 react-native-nitro-modules@0.35.9: 825622aae63a8fb5b904f3c77908a0e216261d727ea171709f2c0b6088422675 react-native-reanimated@4.5.1: a23baea5d82cbf1254720110aef6671e39d57a425f8be446756f91ab806eb72c - react-native-screens@4.26.2: 8156dd0f3407822404793cfdaa95639a36b62102f4507c981b8be83600bb382d + react-native-screens@4.26.2: 595114f3f9ce8b9f0d99d1aafe5c12c997b7d75ca9add31d46cbedbfe347b8e0 uniwind@1.11.0: 17d92be2eec71bb6396b402e8d034968e54b28746876d7977cb3139655f42b90 importers: @@ -135,7 +135,7 @@ importers: version: 7.0.2 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) apps/desktop: dependencies: @@ -183,7 +183,7 @@ importers: version: 8.2.0 electron-updater: specifier: ^6.6.2 - version: 6.8.3 + version: 6.8.9 ffi-rs: specifier: 1.3.2 version: 1.3.2 @@ -211,7 +211,7 @@ importers: version: 4.3.3 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) apps/marketing: dependencies: @@ -220,14 +220,14 @@ importers: version: link:../../packages/shared astro: specifier: ^7.0.3 - version: 7.0.3(@astrojs/markdown-remark@7.2.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(aws4fetch@1.0.20)(idb-keyval@6.2.1)(ioredis@5.11.0)(jiti@2.7.0)(rollup@4.61.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0) + version: 7.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@24.12.4)(aws4fetch@1.0.20)(idb-keyval@6.2.1)(ioredis@5.11.0)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0) sharp: specifier: 0.34.5 version: 0.34.5 devDependencies: '@astrojs/check': specifier: ^0.9.7 - version: 0.9.9(prettier@3.8.3)(typescript@6.0.3) + version: 0.9.10(prettier@3.8.3)(typescript@6.0.3) '@vercel/config': specifier: ^0.3.0 version: 0.3.0 @@ -278,7 +278,7 @@ importers: version: 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) '@react-navigation/native-stack': specifier: 7.17.6 - version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(ad1eff2c3e588b799b6541240bb21d97) + version: 7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(f640afa6cd4b9a1dc5422cbafdc82f53) '@shikijs/core': specifier: 4.2.0 version: 4.2.0 @@ -359,7 +359,7 @@ importers: version: 57.0.1(expo@57.0.18) expo-document-picker: specifier: ~57.0.1 - version: 57.0.2(expo@57.0.18) + version: 57.0.1(expo@57.0.18) expo-file-system: specifier: ~57.0.6 version: 57.0.6(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) @@ -377,7 +377,7 @@ importers: version: 57.0.3(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) expo-image-manipulator: specifier: ~57.0.17 - version: 57.0.19(expo@57.0.18) + version: 57.0.17(expo@57.0.18) expo-image-picker: specifier: ~57.0.14 version: 57.0.14(expo@57.0.18) @@ -416,7 +416,7 @@ importers: version: 57.0.19(expo-dev-client@57.0.16(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) expo-video: specifier: ~57.0.3 - version: 57.0.4(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) + version: 57.0.3(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) expo-web-browser: specifier: ~57.0.2 version: 57.0.2(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) @@ -455,7 +455,7 @@ importers: version: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) react-native-screens: specifier: ~4.26.0 - version: 4.26.2(patch_hash=8156dd0f3407822404793cfdaa95639a36b62102f4507c981b8be83600bb382d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) + version: 4.26.2(patch_hash=595114f3f9ce8b9f0d99d1aafe5c12c997b7d75ca9add31d46cbedbfe347b8e0)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) react-native-shiki-engine: specifier: ^0.3.12 version: 0.3.12(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) @@ -504,7 +504,7 @@ importers: dependencies: '@anthropic-ai/claude-agent-sdk': specifier: ^0.3.260 - version: 0.3.278(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3) + version: 0.3.276(@anthropic-ai/sdk@0.93.0(zod@4.6.5))(@modelcontextprotocol/sdk@1.29.0(zod@4.6.5))(zod@4.6.5) '@effect/platform-bun': specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6) @@ -522,7 +522,7 @@ importers: version: 0.9.4(patch_hash=ab9ff544009e1891cfe3930105862d3699007f38922a79f3c98d90018deca368) '@modelcontextprotocol/sdk': specifier: 1.29.0 - version: 1.29.0(zod@4.4.3) + version: 1.29.0(zod@4.6.5) '@opencode-ai/sdk': specifier: ^1.3.15 version: 1.15.13 @@ -604,16 +604,16 @@ importers: version: link:../../packages/effect-codex-app-server vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) apps/web: dependencies: '@base-ui/react': specifier: ^1.4.1 - version: 1.5.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 1.5.0(@date-fns/tz@1.5.0)(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/electron': specifier: 0.0.42 - version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/react': specifier: 6.15.2 version: 6.15.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -640,7 +640,7 @@ importers: version: 3.3.5(patch_hash=ae73c2fbab0e16563b5bb2639ee85b4b72b18b478a425a4fb2e13b8de9a29fa4)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@lexical/react': specifier: ^0.41.0 - version: 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31) + version: 0.41.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.32) '@noble/hashes': specifier: 'catalog:' version: 1.8.0 @@ -734,13 +734,13 @@ importers: version: 4.0.0-rc.112(patch_hash=a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@rolldown/plugin-babel': specifier: ^0.2.0 - version: 0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5) + version: 0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5) '@tailwindcss/vite': specifier: 4.3.3 - version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)) + version: 4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)) '@tanstack/router-plugin': specifier: ^1.161.0 - version: 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)) + version: 1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)) '@types/babel__core': specifier: ^7.20.5 version: 7.20.5 @@ -767,7 +767,7 @@ importers: version: 0.3.0 '@vitejs/plugin-react': specifier: ^6.0.0 - version: 6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(babel-plugin-react-compiler@1.0.0) + version: 6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(babel-plugin-react-compiler@1.0.0) babel-plugin-react-compiler: specifier: 1.0.0 version: 1.0.0 @@ -785,10 +785,10 @@ importers: version: 11.0.5 vite: specifier: npm:@voidzero-dev/vite-plus-core@0.3.0 - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) infra/relay: dependencies: @@ -818,10 +818,10 @@ importers: version: link:../../packages/shared alchemy: specifier: 2.0.0-beta.76 - version: 2.0.0-beta.76(8f001107992eb148f96e7a0a6f857223) + version: 2.0.0-beta.76(943b389e45c63e6617cf6f94957d1aa7) drizzle-orm: specifier: 1.0.0-rc.5-ab785fc - version: 1.0.0-rc.5-ab785fc(7dbc7f7255b0125741da736693a2e1eb) + version: 1.0.0-rc.5-ab785fc(cbff702a876b7c3154959f590880ea77) effect: specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) @@ -843,10 +843,10 @@ importers: version: 1.0.0-rc.5-ab785fc vite: specifier: npm:@voidzero-dev/vite-plus-core@0.3.0 - version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + version: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) oxlint-plugin-t3code: dependencies: @@ -865,7 +865,7 @@ importers: version: 4.0.0-rc.112(patch_hash=a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/client-runtime: dependencies: @@ -902,7 +902,7 @@ importers: version: 2.0.2 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/contracts: dependencies: @@ -915,7 +915,7 @@ importers: version: 4.0.0-rc.112(patch_hash=a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/effect-acp: dependencies: @@ -925,7 +925,7 @@ importers: devDependencies: '@effect/openapi-generator': specifier: 'catalog:' - version: 4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + version: 4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(encoding@0.1.13) '@effect/platform-node': specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6) @@ -937,7 +937,7 @@ importers: version: 24.12.4 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/effect-codex-app-server: dependencies: @@ -947,7 +947,7 @@ importers: devDependencies: '@effect/openapi-generator': specifier: 'catalog:' - version: 4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + version: 4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(encoding@0.1.13) '@effect/platform-node': specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6) @@ -959,7 +959,7 @@ importers: version: 24.12.4 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/shared: dependencies: @@ -993,7 +993,7 @@ importers: version: 24.12.4 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/ssh: dependencies: @@ -1018,7 +1018,7 @@ importers: version: 24.12.4 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages/tailscale: dependencies: @@ -1037,7 +1037,7 @@ importers: version: 24.12.4 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) scripts: dependencies: @@ -1080,18 +1080,18 @@ importers: version: typescript@6.0.3 vite-plus: specifier: 'catalog:' - version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + version: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) packages: - '@ai-sdk/provider-utils@4.0.51': - resolution: {integrity: sha512-ukLTs9x1Xm6lxSIwbJIxYQxbZHmVeIczNntARZrBcOa6pjdBhqeZnATNnJMHa7M9dZ8Ji5NJbpKpvz7o5XyBtg==} + '@ai-sdk/provider-utils@4.0.49': + resolution: {integrity: sha512-8e7pd+82bobqrFOaD5dG/PiEuvLYr5olaE3I56ch0jipR0H7sGD6ohwTUynv6k8O8QidWiyIbEsZCtr/2dyXIA==} engines: {node: '>=18.17'} peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider@3.0.16': - resolution: {integrity: sha512-9Av6kg0t/IN/dcYAAmEJ4B9OPhcEJqwSR+GfHEA8olRkindXpUHadc3p3cyvgFUQFTVm1G5thtsmgZ9yVb2w3A==} + '@ai-sdk/provider@3.0.15': + resolution: {integrity: sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q==} engines: {node: '>=18'} '@alcalzone/ansi-tokenize@0.2.5': @@ -1125,8 +1125,8 @@ packages: '@alchemy.run/node-utils@2.0.0-beta.76': resolution: {integrity: sha512-KUAlGdoeWeMEToUP1V3I+mEf6FVzUjU+6Tc8savNvMsXN9t3uwZVqD1FeMzTMzMTJJijjxVljQTCtyoeE9W43w==} - '@anthropic-ai/claude-agent-sdk@0.3.278': - resolution: {integrity: sha512-hXfjyiGraqYul6gq7R7+WmktxJfy9S29Yrx1WK6jQpsYcipf1CYIoYSh22SpGOR3FGiEmeuzXg5U9ncOk19Fww==} + '@anthropic-ai/claude-agent-sdk@0.3.276': + resolution: {integrity: sha512-Dic43v4uuGLhibPAArWy3RSfq3zHdn+4Oh2AI2/18a5tlbNKkvChEpazyp8c+grUJvzxkzt0jETCa1wrADbkyw==} engines: {node: '>=18.0.0'} peerDependencies: '@anthropic-ai/sdk': '>=0.93.0' @@ -1142,81 +1142,88 @@ packages: zod: optional: true - '@astrojs/check@0.9.9': - resolution: {integrity: sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==} + '@astrojs/check@0.9.10': + resolution: {integrity: sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==} hasBin: true peerDependencies: typescript: ^5.0.0 || ^6.0.0 - '@astrojs/compiler-binding-darwin-arm64@0.2.3': - resolution: {integrity: sha512-sJIHeL1ONXEBLob8ZaXfmX6iCftUno08G/cMXj2FJnL0xNbHuELcEq1mjxHVFHNgUYu4P7xJNm2mpc0zUEPoKw==} + '@astrojs/compiler-binding-android-arm64@0.4.1': + resolution: {integrity: sha512-Wtx2DZORuNTLASLLZ8hlrjFaJe1LUloatcCn9EPq7knBhRpjMGXeJANIJMeBZG4igvz7x+3nyAJM2iwCSW9xog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@astrojs/compiler-binding-darwin-arm64@0.4.1': + resolution: {integrity: sha512-7iwXcU+hB60Y1SvOfnWHNioT72gDsDQKaDipXh2Onwp/TvQMozhbvhy13wJR8Af940a0h6PbddCeJFpj791UDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@astrojs/compiler-binding-darwin-x64@0.2.3': - resolution: {integrity: sha512-P0NYu6aaIeLCqFfszxxBHL0a5WRaYigNVbDoO654Gi5Q2au5duDb5xZBv5EqUg4qnQVC173FXNvGZu1M7nk+/w==} + '@astrojs/compiler-binding-darwin-x64@0.4.1': + resolution: {integrity: sha512-pifjICos49RXhSVKSDu5gDB0/8j0O7DmxSp1DaWW2PyLG5ekwSkcrfOZto5JqhdI7QYMZB3XOiXpsdOXpS4npA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@astrojs/compiler-binding-linux-arm64-gnu@0.2.3': - resolution: {integrity: sha512-PqVN5AqhuDqfx3ejaerwrC8codpV9jnyKV+IOel027qsJ1anFUJLdjUlY8VVys0xgd8lmqveX11OkcaQj/otTg==} + '@astrojs/compiler-binding-linux-arm64-gnu@0.4.1': + resolution: {integrity: sha512-hsZMlNWc6LqWHh9LlSwTq1/XCY7pxA5rkkKIdZRV8mgbOz9OwLvjEoe5Jry13eIZjMUQO8XnQyzk8o/Pjw2YFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@astrojs/compiler-binding-linux-arm64-musl@0.2.3': - resolution: {integrity: sha512-O3e2CbN4yTsRguWYNnRd0p5YQ0H3fb7KpcR0W4R319q/gq5B1pJ7eqNbiO3b8g2AuiEcRTiUz5jeGT9j69cxOQ==} + '@astrojs/compiler-binding-linux-arm64-musl@0.4.1': + resolution: {integrity: sha512-a3EW27f+Z9uf57vYmpLgUriA1y9wDhKz57PT8D8XlFZt2HZEWhUxKuxI1jvKVI9p8okYee11WXEm/FHY1G9yoQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@astrojs/compiler-binding-linux-x64-gnu@0.2.3': - resolution: {integrity: sha512-hbLBjXVp+96psMe7/7uqyrquGiULXANrq6REVxxPK/I5VzebZ7LHmSfykmByUbLyR1u+K6CTBKgvdQsK2L+2Xw==} + '@astrojs/compiler-binding-linux-x64-gnu@0.4.1': + resolution: {integrity: sha512-XruPTKB/SS4DFzeAjQ1ZyM0ieHDjapHkOSnnzRApZ4FXY2YAI7UY3mETjfJiIS/acyZyrnPH5jObv1gATTdUJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@astrojs/compiler-binding-linux-x64-musl@0.2.3': - resolution: {integrity: sha512-vIiEvOwrJfHZMaTmqUCrFTIwMYL0+PD3Rvy7kFDQgERyx3zhaw8CPa01MCCqa+/sj344BGrXKZ6ti37SgNLMhw==} + '@astrojs/compiler-binding-linux-x64-musl@0.4.1': + resolution: {integrity: sha512-E8j7lGanqNpudtkUbsFaOLJhmwFe4Ecqux7K9hkj4wULIPNZRM2bYZNcMSj8r1oWvaUiwZl8LaaNwv3+14jqhw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@astrojs/compiler-binding-wasm32-wasi@0.2.3': - resolution: {integrity: sha512-p9S2X8z/mUR2SMzAVJRFMCt8YaalKR+pjl2DgpdjzCQc6ww4bo8kiy54tgKqxZeNF5c+/2tCDTQIxVSm9V1FsA==} + '@astrojs/compiler-binding-wasm32-wasi@0.4.1': + resolution: {integrity: sha512-vNLxIv41ZE+nmPFtw24cRW5ZXG+ZPcAGtgigtjCP8lvcxKI7u2GrXoMsCgEH5fMw8WCEJpwLvsVBg15rJKAkyA==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@astrojs/compiler-binding-win32-arm64-msvc@0.2.3': - resolution: {integrity: sha512-vcCG6JttIb5vbSmcxO2O398hpVj7lQ349iS7cjgYP6ZuLVEnw+9qPAr2MM2kJkU5wEGZqJ2gyi/M7UJoPwH1iQ==} + '@astrojs/compiler-binding-win32-arm64-msvc@0.4.1': + resolution: {integrity: sha512-/xTDgRsFDoTh0/+KFXJ74DY4Jqn8CoSHwa8UP5RAblHfnRHkenwfSl4zYDET61O9vXKLk09UzHjjq09yP20/AQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@astrojs/compiler-binding-win32-x64-msvc@0.2.3': - resolution: {integrity: sha512-hKssjNvC36e00Inb1GW1JsVyCFSCGnIjKem4S8q0VIW6cpWAUpvYB4qQU2HIDGD6SDX0ork4F5sWkNWkp2hrGQ==} + '@astrojs/compiler-binding-win32-x64-msvc@0.4.1': + resolution: {integrity: sha512-0x5J6iHZO0Fjo/CVzoIbXKzWnGESftDbEUQ/oXQ61HhUAFoR7+T1jfToZSNAS+gYyBXHSTpMWYC7lzarbhClcg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@astrojs/compiler-binding@0.2.3': - resolution: {integrity: sha512-Xz3iBNse+hXXD25IXxsuXEt2ai8klAWE15CRm/EQBc9+aE3jXaF07DZx+iakk3HC6NHvWlEPzLPyxsLgPzOJsw==} + '@astrojs/compiler-binding@0.4.1': + resolution: {integrity: sha512-ZYVDW1P58OXyxqZbOMp+/2U9KA7y2esIx9r4pWdpZ/l6fCZhaw4ul+D7EgpRVxYLkyTWqzANbTTUFGPFP8zDHA==} engines: {node: ^20.19.0 || >=22.12.0} - '@astrojs/compiler-rs@0.2.3': - resolution: {integrity: sha512-JRAtRcPxS4JeAZEIQFQ6GecBs/Wyp4m6/E8vBNxSgVfo1AtRVLUqRCl5oCGOZ0X/BSBB3Vef/7IlzyiGKi2ORA==} + '@astrojs/compiler-rs@0.4.1': + resolution: {integrity: sha512-//NmAuRhy7eU9iP0ceI5Pymy55M+nUZ//kS6XeOyhBWdDib2KbJOyFaVJ52k7sDkJwTKVox7oPu3SpYiVnGm5A==} + engines: {node: '>=22.12.0'} '@astrojs/compiler@2.13.1': resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} - '@astrojs/internal-helpers@0.10.0': - resolution: {integrity: sha512-Ry2R3VPeIN4uPCSA4xQc+e+vsJXkalKpEbDc07hV+a/o5Bs2N/s/uDcPJH/05L19DKh9tAy7e6JM3YZ6Cxfezw==} + '@astrojs/internal-helpers@0.10.4': + resolution: {integrity: sha512-nozZSy/mKYLqe4YrqbKtdOszedAfXYCtw3wZ0d+CAjz4GqQ4L9rl1ltIL5BlgwmYVinJg/RZ0MgGuWOdlyRZlA==} '@astrojs/language-server@2.16.10': resolution: {integrity: sha512-87VQ/5GSdHlRnUA+hGuerYyIGAj+9RbZmATyuKLEUePinUXhQ5YkRnRrHhOD9sSi5JOErLjrLkHnfZFEvGrV8w==} @@ -1230,18 +1237,15 @@ packages: prettier-plugin-astro: optional: true - '@astrojs/markdown-remark@7.2.0': - resolution: {integrity: sha512-+YxmVQu1Bd+MFfSzjq1rOJvD9+nIOJzz5YIIhdIH01RrxRkKbyKoEgyIqP3yv51MhzMDgd79QaPv+kCVPT8vHw==} - - '@astrojs/markdown-satteri@0.3.2': - resolution: {integrity: sha512-feXuUPy41gVfeM7EHT1ciUim8ozGr+YHXab9uUBc1Hk8y60DQosO8ldL+AoPXnCAoGj1OChwHfvXmmJ6XVnY9A==} + '@astrojs/markdown-satteri@0.3.8': + resolution: {integrity: sha512-n8ItpFTCmlDsVR5+rwDmehSf+jFCYLWmZiisZNGuF7xILqYhsVeBfcha4qgS2Seq3fAc9Tm58ZVrvqFQ6RQgRQ==} '@astrojs/prism@4.0.2': resolution: {integrity: sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==} engines: {node: '>=22.12.0'} - '@astrojs/telemetry@3.3.2': - resolution: {integrity: sha512-j8DNruA8ors99Al39RYZPJK4DC1bKkoNm93mAMuBhY9TCNC4R8n1q7ovFnJ5qhGh5Lsh7pa1gpQVpYpsJPeTHQ==} + '@astrojs/telemetry@3.3.3': + resolution: {integrity: sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} '@astrojs/yaml2ts@0.2.4': @@ -1324,8 +1328,8 @@ packages: resolution: {integrity: sha512-fvHh53zSm2FoQPgkw9thH5D7sd13bC0nPyuZb+mQJ85l5v7lQnsZ97u6e6YkJJN/LU1Mxm1/DLGrIIRR2L7tZw==} engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.973.10': - resolution: {integrity: sha512-992QrTO7G9qCvKD0fx1rMlqcL14plUcRAbwmqqYVsuF3GrqcvlAL9qxR+baMafarEZ+l7DUQ5lCMmt5mbMhF7g==} + '@aws-sdk/types@3.974.5': + resolution: {integrity: sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==} engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.5': @@ -1448,6 +1452,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-proposal-decorators@7.29.7': resolution: {integrity: sha512-EtU0Hi3GvrTqD56xKmZvV/uCXK2ZbwVNPNLAquVItcAZpUhkXwWlo3Fmj0c2LxgSf2I8IDULeAepwNP1OefLXg==} engines: {node: '>=6.9.0'} @@ -1731,6 +1740,10 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@base-ui/react@1.5.0': resolution: {integrity: sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==} engines: {node: '>=14.0.0'} @@ -1761,52 +1774,52 @@ packages: '@blazediff/core@1.9.1': resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} - '@bruits/satteri-darwin-arm64@0.9.3': - resolution: {integrity: sha512-dRUZZrdwh1asfTOyM1nDNmzolhnHtlIFpqYrl1Tdd3YVcaebKmrfJgGL7NAoGPjbEwYmZxaugrxA0uzw83c0dw==} + '@bruits/satteri-darwin-arm64@0.10.5': + resolution: {integrity: sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==} cpu: [arm64] os: [darwin] - '@bruits/satteri-darwin-x64@0.9.3': - resolution: {integrity: sha512-wgNCTRp2hPSpNMGFv5A4+6+VXgRJIlBZ7XKb3iwjV8YjRWNIjzE5zV2fUeYynyZYVRkuJ9aYFqQmWhc1e5H+UQ==} + '@bruits/satteri-darwin-x64@0.10.5': + resolution: {integrity: sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==} cpu: [x64] os: [darwin] - '@bruits/satteri-linux-arm64-gnu@0.9.3': - resolution: {integrity: sha512-A/pWy8Jb/PhDYc2/JFuYh06gFJcsfBUBDl81YydGYBrL/Z4nItDfhNDNOibyeSN/lKKDRlycIHEIajjErk00sQ==} + '@bruits/satteri-linux-arm64-gnu@0.10.5': + resolution: {integrity: sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==} cpu: [arm64] os: [linux] libc: [glibc] - '@bruits/satteri-linux-arm64-musl@0.9.3': - resolution: {integrity: sha512-L6YxmyOSickzo4pE5WmZfNTJnjX0MtgKOsuwQfNZECTx9Ir5vl2B37EIwnxe2AybuPPHl+FqVQtthNDUdH4Vgg==} + '@bruits/satteri-linux-arm64-musl@0.10.5': + resolution: {integrity: sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==} cpu: [arm64] os: [linux] libc: [musl] - '@bruits/satteri-linux-x64-gnu@0.9.3': - resolution: {integrity: sha512-RgH6GPihg9Lzs2yHUsMjqiLxfLyOdmBty8sg9pBY9B4CBnvdOzvg8vklqN+C4qrEEdA9TwpbDpHr1AshLKyRpw==} + '@bruits/satteri-linux-x64-gnu@0.10.5': + resolution: {integrity: sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==} cpu: [x64] os: [linux] libc: [glibc] - '@bruits/satteri-linux-x64-musl@0.9.3': - resolution: {integrity: sha512-BeWhVORjNTIomePznUKiMbHZTqC0j7sMXZFsISmbX+po5d33KLkqBqKh6K332CHJ8KUmCWx16FfPjwsoysttQg==} + '@bruits/satteri-linux-x64-musl@0.10.5': + resolution: {integrity: sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==} cpu: [x64] os: [linux] libc: [musl] - '@bruits/satteri-wasm32-wasi@0.9.3': - resolution: {integrity: sha512-dFNcOHKWV2cztCPnYTn7kZ9D7kNOt8N239z5ysFkNHLxJrfK7zaKIXQbfXYN32C+JoVFqAcTIOeWH2+VnsCOHg==} + '@bruits/satteri-wasm32-wasi@0.10.5': + resolution: {integrity: sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@bruits/satteri-win32-arm64-msvc@0.9.3': - resolution: {integrity: sha512-VnwjBHiAra/PNNEza8eSZdQiG4A3PtTJJwUDtOPAc6iTs0BWZwZX8+OPUZE7//yQCBhgvEMcI8vpwsAwCb6qGQ==} + '@bruits/satteri-win32-arm64-msvc@0.10.5': + resolution: {integrity: sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==} cpu: [arm64] os: [win32] - '@bruits/satteri-win32-x64-msvc@0.9.3': - resolution: {integrity: sha512-Dsoe4reWe69MyILmMwU6iISIceTW7YIFqbyym7haf9DhUvqkYfMAyp7GMM21JzV0SpG9A2BwzFVP7iq9mmxrpA==} + '@bruits/satteri-win32-x64-msvc@0.10.5': + resolution: {integrity: sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==} cpu: [x64] os: [win32] @@ -1826,20 +1839,12 @@ packages: '@chevrotain/utils@10.5.0': resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} - '@clack/core@1.4.2': - resolution: {integrity: sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==} - engines: {node: '>= 20.12.0'} - - '@clack/core@1.4.3': - resolution: {integrity: sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==} - engines: {node: '>= 20.12.0'} - - '@clack/prompts@1.6.0': - resolution: {integrity: sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==} + '@clack/core@1.5.1': + resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} engines: {node: '>= 20.12.0'} - '@clack/prompts@1.7.0': - resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} + '@clack/prompts@1.8.1': + resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} engines: {node: '>= 20.12.0'} '@clerk/backend@3.17.2': @@ -1991,8 +1996,8 @@ packages: '@cloudflare/workers-types@4.20260604.1': resolution: {integrity: sha512-nVTydUwPcz9WfwZ/6xAmqs8uUVeGbDVmyPvSYrYAo4CQTyi0122SoE1Suw8RvqnN60XXHPChAjDqSIjnr/mbmg==} - '@cloudflare/workers-types@5.20260920.1': - resolution: {integrity: sha512-wP7wbKqfQjFRGhE/tvu7bwvmBBTqJjvrETTvNzF4u5oolVEXt0gy6JxbqcUC2Khz0Cy+M/q7rhOOODng8VpnPw==} + '@cloudflare/workers-types@5.20260907.1': + resolution: {integrity: sha512-HYI7eFb/MOcNzvbUW7ZwHVZL5YFXb4r6yHN3vx8gJCqde7yhn3bhuPKiQPw90UtGWWp7uOI+Que3kf8Svjn5cw==} '@crowecawcaw/xa11y-darwin-arm64@0.13.0': resolution: {integrity: sha512-wT+f9lbE6IfSPs8unb7IHr6/V/EkB4SCMFisTtV3jsTseCKr4/vkgpI1LRdul2r+KrmgQ4JxRpUDVB25dmpqNw==} @@ -2036,6 +2041,9 @@ packages: resolution: {integrity: sha512-WTYHwlBhImGmmMt81crxaRvNdZXlBNemFyrP5GGHj2WeDTJMBrHgHTaladTypXHXrviFFBBGROl5moijRQ4ODA==} engines: {node: '>=18'} + '@date-fns/tz@1.5.0': + resolution: {integrity: sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==} + '@distilled.cloud/aws@1.0.0-rc.8': resolution: {integrity: sha512-eL4tRACU4TSykk/6+ItwUTZs93r3tRlrBn36ohqaUxqYYOfmssYlt/SLCzos4nlLgCxb+1T6BAstgYC848v1EQ==} peerDependencies: @@ -2144,8 +2152,8 @@ packages: effect: 4.0.0-rc.112 redis: '>=5.0.0 <7.0.0' - '@effect/sql-d1@4.0.0-rc.116': - resolution: {integrity: sha512-P7jhRWb64pBtCxUo4j319UDAR0DVLQ3t3rPMxTHWQINe4UV4VW6VFMK2KdTFs6SNsurzF4kZA2tVsGpMTIS/eQ==} + '@effect/sql-d1@4.0.0-rc.115': + resolution: {integrity: sha512-o2JkGjN02NBMxwDtJbwcC4teyMiOApqN085fdVMid4FUC6ixj/WyKyPpe51QGOMQQTTl2BkXccA/IwDIywGXlA==} peerDependencies: effect: 4.0.0-rc.112 @@ -2164,8 +2172,8 @@ packages: peerDependencies: effect: 4.0.0-rc.112 - '@effect/sql-sqlite-do@4.0.0-rc.116': - resolution: {integrity: sha512-VYHPcyDtiq6h/vmJD/9c6BFvEi1xqG5c54wlbs15zMjhUybJkGWkhZ5dbs4jCqVhkBNJSYX861oDaXxyH1HELw==} + '@effect/sql-sqlite-do@4.0.0-rc.115': + resolution: {integrity: sha512-7lH9h+w4DW2tI+dus4hEac5mSCy1wgiT+uw/VvjR8YhNDemd3zEFY03NkbD81ipfOpnu2QA1YwgM58ExyPlhug==} peerDependencies: effect: 4.0.0-rc.112 @@ -2337,8 +2345,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2349,8 +2357,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2361,8 +2369,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2373,8 +2381,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2385,8 +2393,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2397,8 +2405,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2409,8 +2417,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2421,8 +2429,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2433,8 +2441,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2445,8 +2453,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2457,8 +2465,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2469,8 +2477,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2481,8 +2489,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2493,8 +2501,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2505,8 +2513,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2517,8 +2525,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2529,8 +2537,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -2541,8 +2549,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2553,8 +2561,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -2565,8 +2573,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2577,8 +2585,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -2589,8 +2597,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2601,8 +2609,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2613,8 +2621,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2625,8 +2633,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2637,8 +2645,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2901,17 +2909,29 @@ packages: '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + '@floating-ui/dom@1.7.6': resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + '@floating-ui/react-dom@2.1.8': resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react@0.27.19': - resolution: {integrity: sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==} + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.20': + resolution: {integrity: sha512-CMqMy7OaXl9W0eq1Uy7L7i2Y/anPvHmFmESd2CEw0t5YvZhcVCeo4MBevAmswRllX7Y2dEidA4ozGPunLSTQpw==} peerDependencies: react: '>=17.0.0' react-dom: '>=17.0.0' @@ -2919,6 +2939,9 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@formkit/auto-animate@0.9.0': resolution: {integrity: sha512-VhP4zEAacXS3dfTpJpJ88QdLqMTcabMg0jwpOSxZ/VzfQVfl3GkZSCZThhGC5uhq/TxPHPzW0dzr4H9Bb1OgKA==} @@ -2926,8 +2949,8 @@ packages: resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} engines: {node: ^20.17.0 || >=22.9.0} - '@hono/node-server@1.19.14': - resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + '@hono/node-server@1.19.17': + resolution: {integrity: sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -3319,8 +3342,8 @@ packages: '@jridgewell/source-map@0.3.11': resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -3621,6 +3644,13 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.2.4': + resolution: {integrity: sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} @@ -3643,6 +3673,9 @@ packages: '@nodable/entities@2.1.1': resolution: {integrity: sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==} + '@nodable/entities@3.0.0': + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -4322,8 +4355,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@preact/signals-core@1.14.2': - resolution: {integrity: sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==} + '@preact/signals-core@1.14.4': + resolution: {integrity: sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==} '@prisma/debug@7.2.0': resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} @@ -4903,153 +4936,6 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rollup/pluginutils@5.4.0': - resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - - '@rollup/rollup-android-arm-eabi@4.61.0': - resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.61.0': - resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.61.0': - resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.61.0': - resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.61.0': - resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.61.0': - resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': - resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.61.0': - resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.61.0': - resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.61.0': - resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.61.0': - resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.61.0': - resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.61.0': - resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.61.0': - resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.61.0': - resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.61.0': - resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.61.0': - resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.61.0': - resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.61.0': - resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.61.0': - resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.61.0': - resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.61.0': - resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.61.0': - resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.61.0': - resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.61.0': - resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==} - cpu: [x64] - os: [win32] - '@shikijs/core@4.2.0': resolution: {integrity: sha512-Hc87Ab1Ld/vEbZRCbwx344I5v+4RU8CVToUTRkqXL1+TjbuOp9U5Xa0M23V4GEWHxVn+yO5otb+HkQVm3ptWQQ==} engines: {node: '>=20'} @@ -5120,8 +5006,8 @@ packages: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} - '@smithy/core@3.24.6': - resolution: {integrity: sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==} + '@smithy/core@3.34.1': + resolution: {integrity: sha512-dLcOUxz8YCv1RZUMKq6GbyUf95pLbrqh34bPvpCZ1+CByFF31BEAFewZjsGCnVsZTKdThNENfGyAgk2TJqVwSw==} engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.3.8': @@ -5136,28 +5022,28 @@ packages: resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/node-config-provider@4.4.6': - resolution: {integrity: sha512-M+gG6eQ0y073mSmNB+erRXJvwpsqsN72ol2w6vcd8FEKeG7pqYK0JvzfVqONkPj2ElBB2pg+cU13I850b//Wag==} + '@smithy/node-config-provider@4.6.2': + resolution: {integrity: sha512-zMrXu/O5tPa7GLtra8L4wFG6DACcXT9QV4Ay+WEAjUhXm1dVq7c/q9Qv9gkJZNLY8hmQKg08778kDcxpKNMqOA==} engines: {node: '>=18.0.0'} '@smithy/node-http-handler@4.7.7': resolution: {integrity: sha512-ZAFvHXrEk6K180EVhmZVg8GU5pUH5BSFqRs27JW3j1qEFx9YyYwWFx17x/MHcjALYimGAji7qEOlF1++be+G5A==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.5.6': - resolution: {integrity: sha512-In8gYD2R66EKlGAq9QrNKVrMOGaGBD7LUNp2kUjeQ4V9zNktFIXBPmrCySr4YYo+jVeVL6CnWj26sOamcF0qIg==} + '@smithy/shared-ini-file-loader@4.7.2': + resolution: {integrity: sha512-XsIDj5gVG4YRxGS4n4TiBDAogPWRHXKZyor6JW/sEuaa/7BvKADe9j45jI2dPYCnYbKkLBmbZ4qN9ns0sH+kMQ==} engines: {node: '>=18.0.0'} '@smithy/signature-v4@5.4.6': resolution: {integrity: sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.14.3': - resolution: {integrity: sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ==} + '@smithy/types@4.18.0': + resolution: {integrity: sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.4.6': - resolution: {integrity: sha512-V6ApAGvCQnb7Wy1Sy60AQc+7UOEaNQxvAXBLdMi5Zzm66cmX0srvfAxDmg7BGuJ+9H9ez0PPWS/AeFgWxwGavA==} + '@smithy/util-base64@4.6.2': + resolution: {integrity: sha512-wTQX1hPfElIqY6AzM/s6c4UgpMxCL4MwJ5u6340ksLPq78lur6Y+keheIDk5gMX4G3KFh4MERcDt6xhRq+ie1Q==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': @@ -5321,8 +5207,8 @@ packages: resolution: {integrity: sha512-qhCRSFei0hokQr3xYcQXqxsRD/LKlgHCxHXtKHrQoImp4x2Zu6tUOpUGVH4y2qexIrzSu3aibQBNNfC3Eay6Mg==} engines: {node: '>=18'} - '@tanstack/query-core@5.103.1': - resolution: {integrity: sha512-rms8HqTGp6zA00dM+cUQ2eBcgzNJefuu5CAMB37i/6MiGT1zulPOytCFu2a0qjLqVR2n1jENPj9woqFQNuCzWA==} + '@tanstack/query-core@5.102.8': + resolution: {integrity: sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==} '@tanstack/react-pacer@0.19.4': resolution: {integrity: sha512-coj8ULAuR0qFpjAKD44gTgRuZyjxU6Xu+IX5MwwYvr4e61OtZcJshaExoOBKpCGde0Edb12jDnzzj2Im13Qm9Q==} @@ -5414,6 +5300,9 @@ packages: '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -5483,6 +5372,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + '@types/http-cache-semantics@4.2.0': resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} @@ -5895,15 +5787,13 @@ packages: '@vscode/l10n@0.0.18': resolution: {integrity: sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==} - '@xmldom/xmldom@0.8.13': - resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} + '@xmldom/xmldom@0.8.15': + resolution: {integrity: sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==} engines: {node: '>=10.0.0'} - deprecated: this version has critical issues, please update to the latest version - '@xmldom/xmldom@0.9.10': - resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} + '@xmldom/xmldom@0.9.12': + resolution: {integrity: sha512-5AXjrcMClTryPe9LgZrygpB1lj7s0S9E0+W+AHaVKAVyHanafK86iPSvG5xHVSp/jC+VH1UXu0TAEmY279xH7A==} engines: {node: '>=14.6'} - deprecated: this version has critical issues, please update to the latest version '@yuku-codegen/binding-darwin-arm64@0.5.48': resolution: {integrity: sha512-yo96Oef12WzqnphInfz/eexVse3+kWgfGS5g2S3rFS3dcGn1ENW9xLFDZUP9rh+yP76DOq38wBoFi1+I9+6qBg==} @@ -6278,6 +6168,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + anynum@1.0.1: + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + app-builder-lib@26.15.6: resolution: {integrity: sha512-lN6BliNJLnS2ruBdYd1j1rzd2yl4ZwKqyTtAwVznIkkadOR9cFvRz/tNQ6nd824AobnFZKhcdFrpIGDnr0PRSg==} engines: {node: '>=14.0.0'} @@ -6305,9 +6198,6 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-iterate@2.0.1: - resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} - asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -6319,12 +6209,12 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - astro@7.0.3: - resolution: {integrity: sha512-CK+G+Tl2DMV1EXCwVG45vyurxf2IfRTklMxDhRKn+tst9Yl8rWXpudL62Fa6zin5Bt968FBvuyASj1aJShROZg==} + astro@7.2.8: + resolution: {integrity: sha512-19nfgzl1IHNYzIfamUIr4dYSQcovWfMO5JGlN7Ahlg6Q6R+nzSPWAh+VO/mSg0hhcea35JHY0wL5ADZUi0WiYQ==} engines: {node: '>=22.12.0', npm: '>=9.6.5', pnpm: '>=7.1.0'} hasBin: true peerDependencies: - '@astrojs/markdown-remark': 7.2.0 + '@astrojs/markdown-remark': 7.2.4 peerDependenciesMeta: '@astrojs/markdown-remark': optional: true @@ -6438,8 +6328,8 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.10.33: - resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==} + baseline-browser-mapping@2.11.24: + resolution: {integrity: sha512-hYrgxie335U08WqICoGqKRzV1HFXv6zdxwJE4ekCb80CM9a0SVVsN4QPwT67RraRo+9h8IATk6uxHJw7QSkdOg==} engines: {node: '>=6.0.0'} hasBin: true @@ -6483,15 +6373,15 @@ packages: resolution: {integrity: sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==} engines: {node: '>= 5.10.0'} - brace-expansion@1.1.15: - resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -6500,8 +6390,8 @@ packages: browser-tabs-lock@1.3.0: resolution: {integrity: sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw==} - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -6519,10 +6409,6 @@ packages: resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} - builder-util-runtime@9.5.1: - resolution: {integrity: sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==} - engines: {node: '>=12.0.0'} - builder-util-runtime@9.7.0: resolution: {integrity: sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==} engines: {node: '>=12.0.0'} @@ -6569,8 +6455,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001793: - resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} capnp-es@0.0.14: resolution: {integrity: sha512-8lWj4GJISiqRSlAJGkWpI4Azib7QY5UDIkqxeHcI7aAnXVk9SFuWxl1Fme+2HhzNANV0WTJqwUZvvXvloc3sBA==} @@ -6840,6 +6726,10 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} @@ -6875,6 +6765,9 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-select@6.0.0: + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} + css-tree@1.1.3: resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} engines: {node: '>=8.0.0'} @@ -6891,6 +6784,10 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} + css-what@7.0.0: + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + engines: {node: '>= 6'} + csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -6902,6 +6799,9 @@ packages: resolution: {integrity: sha512-1+BhOB8ahCn4O0cep0Sh2l9KCOfOdY+BXJnKMHFFzDEouSr/el18QwXEMRlOj9UY5nCeA8UN3a/82rUWRBeyBw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + dbus-next@0.10.2: resolution: {integrity: sha512-kLNQoadPstLgKKGIXKrnRsMgtAK/o+ix3ZmcfTfvBHzghiO9yHXpoKImGnB50EXwnfSFaSAullW/7UrSkAISSQ==} @@ -7004,8 +6904,8 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - devalue@5.8.1: - resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} + devalue@5.9.2: + resolution: {integrity: sha512-po4PAY5c53tw5XMocSnf8A/5OHhbbUftpr93aEN6BBoAdntUmK7vu7wOATqvt7cXO7m1Cl4gMVn6p7n6n4mj0w==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} @@ -7247,11 +7147,11 @@ packages: electron-store@8.2.0: resolution: {integrity: sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==} - electron-to-chromium@1.5.364: - resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==} + electron-to-chromium@1.5.430: + resolution: {integrity: sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==} - electron-updater@6.8.3: - resolution: {integrity: sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==} + electron-updater@6.8.9: + resolution: {integrity: sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==} electron-winstaller@5.4.0: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} @@ -7262,6 +7162,11 @@ packages: engines: {node: '>= 22.12.0'} hasBin: true + electron@44.4.2: + resolution: {integrity: sha512-872t/+8M8TyribWm2cYpRwHY2aDlyDHOn+CHnhN9kHQStVW8pAijIhFolYA/qQyDdri28Xp8Mi3HEj5kFvajCw==} + engines: {node: '>= 22.12.0'} + hasBin: true + emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} @@ -7279,11 +7184,14 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - enhanced-resolve@5.25.1: - resolution: {integrity: sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==} + enhanced-resolve@5.24.5: + resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -7320,8 +7228,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -7331,8 +7239,8 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-toolkit@1.47.0: - resolution: {integrity: sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw==} + es-toolkit@1.52.0: + resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -7345,8 +7253,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} hasBin: true @@ -7376,9 +7284,6 @@ packages: estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -7502,8 +7407,8 @@ packages: peerDependencies: expo: '*' - expo-document-picker@57.0.2: - resolution: {integrity: sha512-vVBbBNg0piMJUIxjB+DR6JaZZpcSl90ZUoywLA/EgNHTfRkZW+9axP97UxM2a0Zm0W6WfH3cLzzGAEfP9OKGwg==} + expo-document-picker@57.0.1: + resolution: {integrity: sha512-qBwM5oxDZ3I9kwFD3pUE1oK/WNv9artoEKO6UpqhQgNRr0XA1ALRVWYjkF4+ge9lUNDRehjTm/jenINkzqg84g==} peerDependencies: expo: '*' @@ -7540,8 +7445,8 @@ packages: peerDependencies: expo: '*' - expo-image-manipulator@57.0.19: - resolution: {integrity: sha512-AxqWNpKTmtyPQwMnWj4T834slKBJ7VzHx88N8b62zKBizCUC7xyEYOvUwRTOutHrADIHuN6P0PUYZxiN7+6XbQ==} + expo-image-manipulator@57.0.17: + resolution: {integrity: sha512-VpM8qAotTeSIobIAL8RAIDrZKL0jQBK/6O41NnwmdT96sd2wT263DlTbkvQ6RkcFrR9+NZiC5Um7FDBK+jFneg==} peerDependencies: expo: '*' @@ -7564,8 +7469,8 @@ packages: expo-json-utils@57.0.1: resolution: {integrity: sha512-cgTe1NqzQdYs/WN+3nIY5IZg8s0pb0xaTUbhYvxQDn137GbwRfHoGM2se3m3Vsl4Qu+B9G4RPEK5WJDEU2Do7g==} - expo-keep-awake@57.0.1: - resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} + expo-keep-awake@57.0.2: + resolution: {integrity: sha512-GqgH746wtJImmsbyHmCQoi7cOslKuWQBbJHXr53S35Bpf5UvxCAztvnitUEd7D0QvFN2rvkOhylbZpgZZfBLlQ==} peerDependencies: expo: '*' react: '*' @@ -7681,8 +7586,8 @@ packages: expo-dev-client: optional: true - expo-video@57.0.4: - resolution: {integrity: sha512-4YCWG1IJzUNpLX0xoussK0VOG524qgUiMbwJzrgpDuPUzOgR3ZqXSZxKsy3HlYLiuWdzIQzwfpbtSNim9F9D3Q==} + expo-video@57.0.3: + resolution: {integrity: sha512-Z+rLdBSzICwoHm/HUxND5fm5nfgqiB+QPWAOKxmA8ScYwvxG1UGjZ6OaayvPc3GkT4aucsbycmFO0uUv/qnIhg==} peerDependencies: expo: '*' react: '*' @@ -7740,8 +7645,8 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - fast-check@4.9.0: - resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} + fast-check@4.10.2: + resolution: {integrity: sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==} engines: {node: '>=12.17.0'} fast-deep-equal@3.1.3: @@ -7766,8 +7671,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -7775,20 +7680,20 @@ packages: fast-xml-builder@1.2.0: resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} - fast-xml-parser@5.7.3: - resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} + fast-xml-parser@5.11.1: + resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==} hasBin: true - fast-xml-parser@5.8.0: - resolution: {integrity: sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==} + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} hasBin: true fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} fb-dotslash@0.5.8: resolution: {integrity: sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==} @@ -7835,6 +7740,10 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + find-proc@0.1.0: + resolution: {integrity: sha512-OaOpEYv2PiQ7SQ5LIrl+deA1XaWcxEjnpM6VuWXTUvn+teIXxeFTLDmu18/zDQpFmHN4o3oDBX+BT0AGwEhemg==} + engines: {node: ^20.19.0 || >=22.12.0} + find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -7860,12 +7769,12 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} engines: {node: '>= 6'} - formatly@0.7.1: - resolution: {integrity: sha512-ESuCEJE0SGgabl9q+zABI8LhJDmF+8nhpuqDzuM48HfJ9QTD5o6PqFJDFZY7w6f8xsh5lixuj5nJsQNvYOWvKQ==} + formatly@0.7.0: + resolution: {integrity: sha512-7CXJtIIA0zy/u12StsYk25qVKxvdLA2ep2sTNxK3ov0mGNIIDqIvAXDSgTnAfDJFsPfWjuz0WjfYSdpvnLA5Tg==} engines: {node: '>=18.3.0'} hasBin: true @@ -7957,9 +7866,6 @@ packages: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} - get-tsconfig@4.14.0: - resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} - get-tsconfig@4.14.3: resolution: {integrity: sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==} @@ -8044,15 +7950,9 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hast-util-from-html@2.0.3: - resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} - hast-util-from-parse5@8.0.3: resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -8071,9 +7971,6 @@ packages: hast-util-to-parse5@8.0.1: resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} - hast-util-to-text@4.0.2: - resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} - hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} @@ -8118,8 +8015,8 @@ packages: resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} engines: {node: '>=16.9.0'} - hono@4.12.27: - resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + hono@4.13.7: + resolution: {integrity: sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==} engines: {node: '>=16.9.0'} hosted-git-info@4.1.0: @@ -8164,6 +8061,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} @@ -8225,8 +8126,8 @@ packages: resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==} engines: {node: '>=12.22.0'} - ip-address@10.2.0: - resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + ip-address@10.7.2: + resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -8257,11 +8158,6 @@ packages: engines: {node: '>=8'} hasBin: true - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-docker@4.0.0: resolution: {integrity: sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==} engines: {node: '>=20'} @@ -8291,11 +8187,6 @@ packages: engines: {node: '>=20'} hasBin: true - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -8325,14 +8216,13 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} + is-unsafe@2.0.2: + resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} - engines: {node: '>=16'} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -8405,8 +8295,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.2.0: - resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} hasBin: true jsbi@2.0.5: @@ -8652,8 +8542,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -8663,8 +8553,8 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lru.min@1.1.4: - resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + lru.min@1.1.5: + resolution: {integrity: sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} lru_map@0.4.1: @@ -8682,8 +8572,11 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.5.3: - resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + magic-string@1.4.1: + resolution: {integrity: sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==} + + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} make-fetch-happen@15.0.6: resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==} @@ -8709,9 +8602,6 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-definitions@6.0.0: - resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} - mdast-util-directive@3.1.0: resolution: {integrity: sha512-I3fNFt+DHmpWCYAT7quoM6lHf9wuqtI+oCOfvILnoicNIqjh5E3dEJWiXuYME2gNe8vl1iMQwyUHa7bgFmak6Q==} @@ -9195,8 +9085,8 @@ packages: resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} engines: {node: '>=8.0.0'} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -9212,8 +9102,8 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - neotraverse@0.6.18: - resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + neotraverse@1.0.1: + resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==} engines: {node: '>= 10'} nlcst-to-string@4.0.0: @@ -9265,8 +9155,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-mock-http@1.0.4: - resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + node-mock-http@1.0.5: + resolution: {integrity: sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==} node-pty@1.1.0: resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==} @@ -9274,8 +9164,8 @@ packages: node-readfiles@0.2.0: resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} - node-releases@2.0.46: - resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + node-releases@2.0.55: + resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} engines: {node: '>=18'} nopt@9.0.0: @@ -9340,15 +9230,15 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + ohash@2.0.12: + resolution: {integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==} on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} @@ -9447,8 +9337,8 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@7.3.0: - resolution: {integrity: sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw==} + p-limit@7.3.2: + resolution: {integrity: sha512-Ll0w3fU24vYpXoZmjjZIee6bJQDgG0oAyo1PdmFYI8UDwJJddaHAypxIH9avUu+t+lSsAwKVsb1jDCMIIChliw==} engines: {node: '>=20'} p-locate@3.0.0: @@ -9459,21 +9349,18 @@ packages: resolution: {integrity: sha512-MitaVsCuCFIvOLLPIU7NnfrZvS9H9h7kwMUkDo+T2pEISaJD48IV9S8iIdXB7PsvvdxyYcsSTTrr90XKsbulNw==} engines: {node: '>=18'} - p-queue@9.3.0: - resolution: {integrity: sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==} + p-queue@9.3.3: + resolution: {integrity: sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==} engines: {node: '>=20'} - p-timeout@7.0.1: - resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} + p-timeout@7.0.2: + resolution: {integrity: sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==} engines: {node: '>=20'} p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - package-manager-detector@1.8.0: resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} @@ -9483,9 +9370,6 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-latin@7.0.0: - resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} - parse-png@2.1.0: resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} engines: {node: '>=10'} @@ -9508,8 +9392,8 @@ packages: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} - path-expression-matcher@1.5.0: - resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + path-expression-matcher@1.6.2: + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} engines: {node: '>=14.0.0'} path-is-absolute@1.0.1: @@ -9647,8 +9531,8 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -9770,8 +9654,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@8.4.0: - resolution: {integrity: sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==} + pure-rand@8.4.2: + resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} @@ -9780,8 +9664,8 @@ packages: resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} engines: {node: '>=16.0.0'} - qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} + qs@6.16.0: + resolution: {integrity: sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==} engines: {node: '>=0.6'} query-string@7.1.3: @@ -9822,10 +9706,14 @@ packages: peerDependencies: react: ^19.2.6 - react-error-boundary@6.1.2: - resolution: {integrity: sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==} + react-error-boundary@6.1.6: + resolution: {integrity: sha512-CDXPnXDGyFIbkwaaJ6u+xgsRmJhSi6YdgUDW1vnyKHfXp1a9pfAlM+ZET2CDu80/A+8iRcmXN0NSY9BCCoWP5A==} peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true react-freeze@1.0.4: resolution: {integrity: sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==} @@ -10085,21 +9973,12 @@ packages: resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} hasBin: true - rehype-parse@9.0.1: - resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} - rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} rehype-sanitize@6.0.0: resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} - rehype-stringify@10.0.1: - resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} - - rehype@13.0.2: - resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} - remark-breaks@4.0.0: resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==} @@ -10112,10 +9991,6 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - remark-smartypants@3.0.2: - resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} - engines: {node: '>=16.0.0'} - remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -10176,18 +10051,9 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - retext-latin@4.0.0: - resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} - retext-smartypants@6.2.0: resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} - retext-stringify@4.0.0: - resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} - - retext@9.0.0: - resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} - retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} @@ -10218,11 +10084,6 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.61.0: - resolution: {integrity: sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -10247,11 +10108,11 @@ packages: sanitize-filename@1.6.4: resolution: {integrity: sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==} - satteri@0.9.3: - resolution: {integrity: sha512-2XfBh89LCnBMFkNOeVKkBLelAZcIA17VLHsgJum1tJ2fXiPZDN/TDXv4ku46rFOQXYd41LJ0kiZh5gPqExcCsg==} + satteri@0.10.5: + resolution: {integrity: sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==} - sax@1.6.0: - resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} engines: {node: '>=11.0.0'} scheduler@0.27.0: @@ -10347,8 +10208,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.4: - resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} shiki@4.2.0: @@ -10428,10 +10289,6 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smol-toml@1.7.0: - resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==} - engines: {node: '>= 18'} - smol-toml@1.8.0: resolution: {integrity: sha512-kCZr2V3ch9i00x8zXRhjUNVcjG9ijES5dDudkXvUVCT5QlJNQWElSJdZqyPemffHoLNUYwOcou0Fy+ojN0uHSQ==} engines: {node: '>= 18'} @@ -10479,8 +10336,8 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - sql-escaper@1.5.1: - resolution: {integrity: sha512-4toX5E1fQbBrpfXidaHnF0669nkAdETeIPTs2SUjxxD7RRIs9ICG4gtpmfc68JCEKehsdwLFqBu9VlQqZ1P1gg==} + sql-escaper@1.5.2: + resolution: {integrity: sha512-6CKD38c31SENivxOADeMNLdukOnUxUcflKtzVWzace7Riv1v7cAEym5Cx9Q7gYZ3ezIJI7ZpqecQq8cqNeSSFg==} engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} ssri@13.0.1: @@ -10587,8 +10444,8 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strnum@2.3.0: - resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + strnum@2.4.2: + resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} structured-headers@0.4.1: resolution: {integrity: sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==} @@ -10623,8 +10480,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svgo@4.0.1: - resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} + svgo@4.1.0: + resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} engines: {node: '>=16'} hasBin: true @@ -10632,8 +10489,8 @@ packages: resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} hasBin: true - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} @@ -10699,8 +10556,8 @@ packages: resolution: {integrity: sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==} engines: {node: ^16.14.0 || >= 17.3.0} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} tinyglobby@0.2.17: @@ -10824,8 +10681,8 @@ packages: ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} - ultrahtml@1.6.0: - resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + ultrahtml@1.7.0: + resolution: {integrity: sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==} unbash@4.0.11: resolution: {integrity: sha512-FoSOKV7NEofQSkAefMVHam4ZPKYMxjAydxiV72UFEDNV/YofxjGfiZ2A9pZjdL/lRJzTjcu4PABo1JYJX8N5iQ==} @@ -10841,8 +10698,8 @@ packages: resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} engines: {node: '>=18.17'} - undici@7.27.1: - resolution: {integrity: sha512-UDdpiex+mzigiyrXrGbiUaF4HzTNhKbh2vRNFaTMzcqmLIPrZxaCtwo/1TMSuWoM1Xz3WiTo9KdgI3kRqYzJGg==} + undici@7.29.1: + resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} engines: {node: '>=20.18.1'} undici@8.10.2: @@ -10871,30 +10728,18 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - unifont@0.7.4: - resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==} - - unist-util-find-after@5.0.0: - resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + unifont@0.7.5: + resolution: {integrity: sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==} unist-util-is@6.0.1: resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - unist-util-modify-children@4.0.0: - resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} - unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - unist-util-remove-position@5.0.0: - resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==} - unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - unist-util-visit-children@3.0.0: - resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} - unist-util-visit-parents@6.0.2: resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} @@ -11015,8 +10860,8 @@ packages: unzipper@0.12.5: resolution: {integrity: sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==} - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -11303,10 +11148,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-pm-runs@1.1.0: - resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} - engines: {node: '>=4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -11363,18 +11204,6 @@ packages: utf-8-validate: optional: true - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.21.3: resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} @@ -11395,6 +11224,10 @@ packages: resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} engines: {node: '>=16.0.0'} + xml-naming@0.3.0: + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} + engines: {node: '>=16.0.0'} + xml2js@0.4.23: resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==} engines: {node: '>=4.0.0'} @@ -11461,8 +11294,8 @@ packages: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} engines: {node: '>=12'} - yjs@13.6.31: - resolution: {integrity: sha512-Eq+5BRfbeGyqGVrTJL3bEcr8gKkxPuyuoHmAwpk52fDb8kOVMrfVSTRPd6yiGgX5Fskb96qCRjzjbRjrL4YEnw==} + yjs@13.6.32: + resolution: {integrity: sha512-lfiJIIC4Xayt5ItynE407ehlE03pCjeOc4hkR4yxxvvNJ4kuiN25B0g+Qp8XagYz361LLL7DCzR5bvFJ81QKtQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} yocto-queue@0.1.0: @@ -11504,6 +11337,9 @@ packages: zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + zustand@5.0.14: resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} engines: {node: '>=12.20.0'} @@ -11532,15 +11368,15 @@ packages: snapshots: - '@ai-sdk/provider-utils@4.0.51(zod@4.4.3)': + '@ai-sdk/provider-utils@4.0.49(zod@4.4.3)': dependencies: - '@ai-sdk/provider': 3.0.16 + '@ai-sdk/provider': 3.0.15 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.1.0 undici: 6.28.1 zod: 4.4.3 - '@ai-sdk/provider@3.0.16': + '@ai-sdk/provider@3.0.15': dependencies: json-schema: 0.4.0 @@ -11549,7 +11385,7 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - '@alchemy.run/cloudflare-runtime@2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)))(@effect/platform-bun@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6))(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(@types/node@24.12.4)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(rolldown@1.2.5)(typescript@7.0.2)': + '@alchemy.run/cloudflare-runtime@2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)))(@effect/platform-bun@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6))(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(@types/node@24.12.4)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(rolldown@1.2.5)(typescript@7.0.2)': dependencies: '@alchemy.run/node-utils': 2.0.0-beta.76 '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260704.1) @@ -11566,7 +11402,7 @@ snapshots: '@effect/platform-bun': 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6) '@effect/platform-node': 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6) rolldown: 1.2.5 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' transitivePeerDependencies: - '@types/node' - proxy-agent @@ -11578,90 +11414,94 @@ snapshots: '@alchemy.run/node-utils@2.0.0-beta.76': {} - '@anthropic-ai/claude-agent-sdk@0.3.278(@anthropic-ai/sdk@0.93.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)': + '@anthropic-ai/claude-agent-sdk@0.3.276(@anthropic-ai/sdk@0.93.0(zod@4.6.5))(@modelcontextprotocol/sdk@1.29.0(zod@4.6.5))(zod@4.6.5)': dependencies: - '@anthropic-ai/sdk': 0.93.0(zod@4.4.3) - '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) - zod: 4.4.3 + '@anthropic-ai/sdk': 0.93.0(zod@4.6.5) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.6.5) + zod: 4.6.5 - '@anthropic-ai/sdk@0.93.0(zod@4.4.3)': + '@anthropic-ai/sdk@0.93.0(zod@4.6.5)': dependencies: json-schema-to-ts: 3.1.1 optionalDependencies: - zod: 4.4.3 + zod: 4.6.5 - '@astrojs/check@0.9.9(prettier@3.8.3)(typescript@6.0.3)': + '@astrojs/check@0.9.10(prettier@3.8.3)(typescript@6.0.3)': dependencies: '@astrojs/language-server': 2.16.10(prettier@3.8.3)(typescript@6.0.3) chokidar: 4.0.3 kleur: 4.1.5 typescript: 6.0.3 - yargs: 17.7.2 + yargs: 18.1.0 transitivePeerDependencies: - prettier - prettier-plugin-astro - '@astrojs/compiler-binding-darwin-arm64@0.2.3': + '@astrojs/compiler-binding-android-arm64@0.4.1': + optional: true + + '@astrojs/compiler-binding-darwin-arm64@0.4.1': optional: true - '@astrojs/compiler-binding-darwin-x64@0.2.3': + '@astrojs/compiler-binding-darwin-x64@0.4.1': optional: true - '@astrojs/compiler-binding-linux-arm64-gnu@0.2.3': + '@astrojs/compiler-binding-linux-arm64-gnu@0.4.1': optional: true - '@astrojs/compiler-binding-linux-arm64-musl@0.2.3': + '@astrojs/compiler-binding-linux-arm64-musl@0.4.1': optional: true - '@astrojs/compiler-binding-linux-x64-gnu@0.2.3': + '@astrojs/compiler-binding-linux-x64-gnu@0.4.1': optional: true - '@astrojs/compiler-binding-linux-x64-musl@0.2.3': + '@astrojs/compiler-binding-linux-x64-musl@0.4.1': optional: true - '@astrojs/compiler-binding-wasm32-wasi@0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-binding-wasm32-wasi@0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': dependencies: - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' optional: true - '@astrojs/compiler-binding-win32-arm64-msvc@0.2.3': + '@astrojs/compiler-binding-win32-arm64-msvc@0.4.1': optional: true - '@astrojs/compiler-binding-win32-x64-msvc@0.2.3': + '@astrojs/compiler-binding-win32-x64-msvc@0.4.1': optional: true - '@astrojs/compiler-binding@0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-binding@0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': optionalDependencies: - '@astrojs/compiler-binding-darwin-arm64': 0.2.3 - '@astrojs/compiler-binding-darwin-x64': 0.2.3 - '@astrojs/compiler-binding-linux-arm64-gnu': 0.2.3 - '@astrojs/compiler-binding-linux-arm64-musl': 0.2.3 - '@astrojs/compiler-binding-linux-x64-gnu': 0.2.3 - '@astrojs/compiler-binding-linux-x64-musl': 0.2.3 - '@astrojs/compiler-binding-wasm32-wasi': 0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - '@astrojs/compiler-binding-win32-arm64-msvc': 0.2.3 - '@astrojs/compiler-binding-win32-x64-msvc': 0.2.3 + '@astrojs/compiler-binding-android-arm64': 0.4.1 + '@astrojs/compiler-binding-darwin-arm64': 0.4.1 + '@astrojs/compiler-binding-darwin-x64': 0.4.1 + '@astrojs/compiler-binding-linux-arm64-gnu': 0.4.1 + '@astrojs/compiler-binding-linux-arm64-musl': 0.4.1 + '@astrojs/compiler-binding-linux-x64-gnu': 0.4.1 + '@astrojs/compiler-binding-linux-x64-musl': 0.4.1 + '@astrojs/compiler-binding-wasm32-wasi': 0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) + '@astrojs/compiler-binding-win32-arm64-msvc': 0.4.1 + '@astrojs/compiler-binding-win32-x64-msvc': 0.4.1 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - '@astrojs/compiler-rs@0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@astrojs/compiler-rs@0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': dependencies: - '@astrojs/compiler-binding': 0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@astrojs/compiler-binding': 0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' '@astrojs/compiler@2.13.1': {} - '@astrojs/internal-helpers@0.10.0': + '@astrojs/internal-helpers@0.10.4': dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 - js-yaml: 4.2.0 + js-yaml: 4.3.2 picomatch: 4.0.7 retext-smartypants: 6.2.0 shiki: 4.2.0 @@ -11672,7 +11512,7 @@ snapshots: dependencies: '@astrojs/compiler': 2.13.1 '@astrojs/yaml2ts': 0.2.4 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@volar/kit': 2.4.28(typescript@6.0.3) '@volar/language-core': 2.4.28 '@volar/language-server': 2.4.28 @@ -11693,47 +11533,23 @@ snapshots: transitivePeerDependencies: - typescript - '@astrojs/markdown-remark@7.2.0': + '@astrojs/markdown-satteri@0.3.8': dependencies: - '@astrojs/internal-helpers': 0.10.0 + '@astrojs/internal-helpers': 0.10.4 '@astrojs/prism': 4.0.2 github-slugger: 2.0.0 - hast-util-from-html: 2.0.3 - hast-util-to-text: 4.0.2 - mdast-util-definitions: 6.0.0 - rehype-raw: 7.0.0 - rehype-stringify: 10.0.1 - remark-gfm: 4.0.1 - remark-parse: 11.0.0 - remark-rehype: 11.1.2 - remark-smartypants: 3.0.2 - unified: 11.0.5 - unist-util-remove-position: 5.0.0 - unist-util-visit: 5.1.0 - unist-util-visit-parents: 6.0.2 - vfile: 6.0.3 - transitivePeerDependencies: - - supports-color - optional: true - - '@astrojs/markdown-satteri@0.3.2': - dependencies: - '@astrojs/internal-helpers': 0.10.0 - '@astrojs/prism': 4.0.2 - github-slugger: 2.0.0 - satteri: 0.9.3 + satteri: 0.10.5 '@astrojs/prism@4.0.2': dependencies: prismjs: 1.30.0 - '@astrojs/telemetry@3.3.2': + '@astrojs/telemetry@3.3.3': dependencies: ci-info: 4.4.0 dset: 3.1.4 is-docker: 4.0.0 - is-wsl: 3.1.1 - which-pm-runs: 1.1.0 + package-manager-detector: 1.8.0 '@astrojs/yaml2ts@0.2.4': dependencies: @@ -11742,7 +11558,7 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 tslib: 2.8.1 '@aws-crypto/sha256-browser@5.2.0': @@ -11750,7 +11566,7 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 '@aws-sdk/util-locate-window': 3.965.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -11758,7 +11574,7 @@ snapshots: '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -11767,7 +11583,7 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -11777,48 +11593,48 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/core': 3.974.17 '@aws-sdk/credential-provider-node': 3.972.51 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/fetch-http-handler': 5.4.6 '@smithy/node-http-handler': 4.7.7 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/core@3.974.17': dependencies: - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 '@aws-sdk/xml-builder': 3.972.27 '@aws/lambda-invoke-store': 0.2.4 - '@smithy/core': 3.24.6 + '@smithy/core': 3.34.1 '@smithy/signature-v4': 5.4.6 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 bowser: 2.14.1 tslib: 2.8.1 '@aws-sdk/credential-provider-cognito-identity@3.972.41': dependencies: '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-env@3.972.43': dependencies: '@aws-sdk/core': 3.974.17 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-http@3.972.45': dependencies: '@aws-sdk/core': 3.974.17 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/fetch-http-handler': 5.4.6 '@smithy/node-http-handler': 4.7.7 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-ini@3.972.49': @@ -11831,19 +11647,19 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.972.48 '@aws-sdk/credential-provider-web-identity': 3.972.48 '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/credential-provider-imds': 4.3.8 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-login@3.972.48': dependencies: '@aws-sdk/core': 3.974.17 '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-node@3.972.51': @@ -11854,18 +11670,18 @@ snapshots: '@aws-sdk/credential-provider-process': 3.972.43 '@aws-sdk/credential-provider-sso': 3.972.48 '@aws-sdk/credential-provider-web-identity': 3.972.48 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/credential-provider-imds': 4.3.8 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-process@3.972.43': dependencies: '@aws-sdk/core': 3.974.17 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-sso@3.972.48': @@ -11873,18 +11689,18 @@ snapshots: '@aws-sdk/core': 3.974.17 '@aws-sdk/nested-clients': 3.997.16 '@aws-sdk/token-providers': 3.1062.0 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-provider-web-identity@3.972.48': dependencies: '@aws-sdk/core': 3.974.17 '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/credential-providers@3.1062.0': @@ -11901,10 +11717,10 @@ snapshots: '@aws-sdk/credential-provider-sso': 3.972.48 '@aws-sdk/credential-provider-web-identity': 3.972.48 '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/credential-provider-imds': 4.3.8 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/nested-clients@3.997.16': @@ -11913,32 +11729,32 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-sdk/core': 3.974.17 '@aws-sdk/signature-v4-multi-region': 3.996.31 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 '@smithy/fetch-http-handler': 5.4.6 '@smithy/node-http-handler': 4.7.7 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/signature-v4-multi-region@3.996.31': dependencies: - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 '@smithy/signature-v4': 5.4.6 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/token-providers@3.1062.0': dependencies: '@aws-sdk/core': 3.974.17 '@aws-sdk/nested-clients': 3.997.16 - '@aws-sdk/types': 3.973.10 - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@aws-sdk/types': 3.974.5 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 - '@aws-sdk/types@3.973.10': + '@aws-sdk/types@3.974.5': dependencies: - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.965.5': @@ -11947,7 +11763,7 @@ snapshots: '@aws-sdk/xml-builder@3.972.27': dependencies: - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 fast-xml-parser: 5.7.3 tslib: 2.8.1 @@ -11968,10 +11784,10 @@ snapshots: '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -11983,21 +11799,21 @@ snapshots: '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.2 + browserslist: 4.29.0 lru-cache: 5.1.1 semver: 6.3.1 @@ -12037,14 +11853,14 @@ snapshots: '@babel/helper-member-expression-to-functions@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -12059,7 +11875,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/helper-plugin-utils@7.29.7': {} @@ -12084,7 +11900,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -12098,18 +11914,22 @@ snapshots: dependencies: '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 '@babel/plugin-proposal-decorators@7.29.7(@babel/core@7.29.7)': dependencies: @@ -12347,7 +12167,7 @@ snapshots: '@babel/helper-module-imports': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -12358,7 +12178,7 @@ snapshots: '@babel/helper-module-imports': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -12428,17 +12248,17 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -12448,7 +12268,12 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.5.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@base-ui/react@1.5.0(@date-fns/tz@1.5.0)(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@babel/runtime': 7.29.7 '@base-ui/utils': 0.2.9(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -12458,7 +12283,9 @@ snapshots: react-dom: 19.2.6(react@19.2.6) use-sync-external-store: 1.6.0(react@19.2.6) optionalDependencies: + '@date-fns/tz': 1.5.0 '@types/react': 19.2.16 + date-fns: 4.4.0 '@base-ui/utils@0.2.9(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -12473,35 +12300,35 @@ snapshots: '@blazediff/core@1.9.1': {} - '@bruits/satteri-darwin-arm64@0.9.3': + '@bruits/satteri-darwin-arm64@0.10.5': optional: true - '@bruits/satteri-darwin-x64@0.9.3': + '@bruits/satteri-darwin-x64@0.10.5': optional: true - '@bruits/satteri-linux-arm64-gnu@0.9.3': + '@bruits/satteri-linux-arm64-gnu@0.10.5': optional: true - '@bruits/satteri-linux-arm64-musl@0.9.3': + '@bruits/satteri-linux-arm64-musl@0.10.5': optional: true - '@bruits/satteri-linux-x64-gnu@0.9.3': + '@bruits/satteri-linux-x64-gnu@0.10.5': optional: true - '@bruits/satteri-linux-x64-musl@0.9.3': + '@bruits/satteri-linux-x64-musl@0.10.5': optional: true - '@bruits/satteri-wasm32-wasi@0.9.3': + '@bruits/satteri-wasm32-wasi@0.10.5': 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) + '@napi-rs/wasm-runtime': 1.2.4(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@bruits/satteri-win32-arm64-msvc@0.9.3': + '@bruits/satteri-win32-arm64-msvc@0.10.5': optional: true - '@bruits/satteri-win32-x64-msvc@0.9.3': + '@bruits/satteri-win32-x64-msvc@0.10.5': optional: true '@capsizecss/unpack@4.0.1': @@ -12523,26 +12350,14 @@ snapshots: '@chevrotain/utils@10.5.0': {} - '@clack/core@1.4.2': - dependencies: - fast-wrap-ansi: 0.2.2 - sisteransi: 1.0.5 - - '@clack/core@1.4.3': - dependencies: - fast-wrap-ansi: 0.2.2 - sisteransi: 1.0.5 - - '@clack/prompts@1.6.0': + '@clack/core@1.5.1': dependencies: - '@clack/core': 1.4.2 - fast-string-width: 3.0.2 fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@clack/prompts@1.7.0': + '@clack/prompts@1.8.1': dependencies: - '@clack/core': 1.4.3 + '@clack/core': 1.5.1 fast-string-width: 3.0.2 fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 @@ -12561,7 +12376,7 @@ snapshots: '@clerk/shared': 4.31.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@stripe/stripe-js': 5.6.0 '@swc/helpers': 0.5.21 - '@tanstack/query-core': 5.103.1 + '@tanstack/query-core': 5.102.8 '@zxcvbn-ts/core': 3.0.4 '@zxcvbn-ts/language-common': 3.0.4 alien-signals: 2.0.6 @@ -12578,7 +12393,7 @@ snapshots: '@clerk/shared': 4.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@stripe/stripe-js': 5.6.0 '@swc/helpers': 0.5.21 - '@tanstack/query-core': 5.103.1 + '@tanstack/query-core': 5.102.8 '@zxcvbn-ts/core': 3.0.4 '@zxcvbn-ts/language-common': 3.0.4 alien-signals: 2.0.6 @@ -12622,7 +12437,20 @@ snapshots: electron-store: 8.2.0 react-dom: 19.2.6(react@19.2.6) - '@clerk/expo@4.6.6(patch_hash=72e426f44fc1cde16fc2cbba3d1e96cdca7c6d957faa73d0fe6b43948608a6c1)(dbc31631339ce74330e188d5d7a88158)': + '@clerk/electron@0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@clerk/clerk-js': 6.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@clerk/react': 6.15.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@clerk/shared': 4.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + electron: 44.4.2 + react: 19.2.6 + tslib: 2.8.1 + optionalDependencies: + '@clerk/electron-passkeys': 0.0.3 + electron-store: 8.2.0 + react-dom: 19.2.6(react@19.2.6) + + '@clerk/expo@4.6.6(patch_hash=72e426f44fc1cde16fc2cbba3d1e96cdca7c6d957faa73d0fe6b43948608a6c1)(dbc31631339ce74330e188d5d7a88158)': dependencies: '@clerk/clerk-js': 6.31.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@clerk/react': 6.15.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -12661,7 +12489,7 @@ snapshots: '@clerk/shared@4.31.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: - '@tanstack/query-core': 5.103.1 + '@tanstack/query-core': 5.102.8 dequal: 2.0.3 glob-to-regexp: 0.4.1 js-cookie: 3.0.8 @@ -12671,7 +12499,7 @@ snapshots: '@clerk/shared@4.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: - '@tanstack/query-core': 5.103.1 + '@tanstack/query-core': 5.102.8 dequal: 2.0.3 glob-to-regexp: 0.4.1 js-cookie: 3.0.8 @@ -12702,7 +12530,7 @@ snapshots: '@cloudflare/workers-types@4.20260604.1': {} - '@cloudflare/workers-types@5.20260920.1': {} + '@cloudflare/workers-types@5.20260907.1': {} '@crowecawcaw/xa11y-darwin-arm64@0.13.0': optional: true @@ -12731,19 +12559,22 @@ snapshots: '@crowecawcaw/xa11y-win32-arm64-msvc': 0.13.0 '@crowecawcaw/xa11y-win32-x64-msvc': 0.13.0 + '@date-fns/tz@1.5.0': + optional: true + '@distilled.cloud/aws@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/util': 5.2.0 '@aws-sdk/credential-providers': 3.1062.0 - '@aws-sdk/types': 3.973.10 + '@aws-sdk/types': 3.974.5 '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) - '@smithy/shared-ini-file-loader': 4.5.6 - '@smithy/types': 4.14.3 - '@smithy/util-base64': 4.4.6 + '@smithy/shared-ini-file-loader': 4.7.2 + '@smithy/types': 4.18.0 + '@smithy/util-base64': 4.6.2 aws4fetch: 1.0.20 effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) - fast-xml-parser: 5.8.0 + fast-xml-parser: 5.11.1 '@distilled.cloud/axiom@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': dependencies: @@ -12830,11 +12661,11 @@ snapshots: react: 19.2.6 scheduler: 0.27.0 - '@effect/openapi-generator@4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': + '@effect/openapi-generator@4.0.0-rc.112(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(encoding@0.1.13)': dependencies: '@effect/platform-node': 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6) effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) - swagger2openapi: 7.0.8 + swagger2openapi: 7.0.8(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -12866,9 +12697,9 @@ snapshots: - bufferutil - utf-8-validate - '@effect/sql-d1@4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': + '@effect/sql-d1@4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': dependencies: - '@cloudflare/workers-types': 5.20260920.1 + '@cloudflare/workers-types': 5.20260907.1 effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) '@effect/sql-mysql2@4.0.0-rc.112(@types/node@24.12.4)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': @@ -12893,7 +12724,7 @@ snapshots: dependencies: effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) - '@effect/sql-sqlite-do@4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': + '@effect/sql-sqlite-do@4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))': dependencies: effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) @@ -12983,7 +12814,7 @@ snapshots: semver: 7.8.5 sumchecker: 3.0.1 optionalDependencies: - undici: 7.27.1 + undici: 7.29.1 transitivePeerDependencies: - supports-color @@ -13125,157 +12956,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.28.1': + '@esbuild/aix-ppc64@0.28.2': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.28.1': + '@esbuild/android-arm64@0.28.2': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.28.1': + '@esbuild/android-arm@0.28.2': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.28.1': + '@esbuild/android-x64@0.28.2': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.28.1': + '@esbuild/darwin-arm64@0.28.2': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.28.1': + '@esbuild/darwin-x64@0.28.2': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.28.1': + '@esbuild/freebsd-arm64@0.28.2': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.28.1': + '@esbuild/freebsd-x64@0.28.2': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.28.1': + '@esbuild/linux-arm64@0.28.2': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.28.1': + '@esbuild/linux-arm@0.28.2': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.28.1': + '@esbuild/linux-ia32@0.28.2': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.28.1': + '@esbuild/linux-loong64@0.28.2': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.28.1': + '@esbuild/linux-mips64el@0.28.2': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.28.1': + '@esbuild/linux-ppc64@0.28.2': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.28.1': + '@esbuild/linux-riscv64@0.28.2': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.28.1': + '@esbuild/linux-s390x@0.28.2': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.28.1': + '@esbuild/linux-x64@0.28.2': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.28.1': + '@esbuild/netbsd-arm64@0.28.2': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.28.1': + '@esbuild/netbsd-x64@0.28.2': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.28.1': + '@esbuild/openbsd-arm64@0.28.2': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.28.1': + '@esbuild/openbsd-x64@0.28.2': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.28.1': + '@esbuild/openharmony-arm64@0.28.2': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.28.1': + '@esbuild/sunos-x64@0.28.2': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.28.1': + '@esbuild/win32-arm64@0.28.2': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.28.1': + '@esbuild/win32-ia32@0.28.2': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.28.1': + '@esbuild/win32-x64@0.28.2': optional: true '@exodus/schemasafe@1.3.0': {} @@ -13306,7 +13137,7 @@ snapshots: '@expo/router-server': 57.0.8(@expo/metro-runtime@57.0.14)(expo-constants@57.0.16(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)))(expo-font@57.0.2(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(expo-server@57.0.3)(expo@57.0.18)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@expo/schema-utils': 57.0.2 '@expo/spawn-async': 1.8.0 - '@expo/ws-tunnel': 2.0.0(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@expo/ws-tunnel': 2.0.0(ws@8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@expo/xcpretty': 4.4.4 '@react-native/dev-middleware': 0.86.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) accepts: 1.3.8 @@ -13330,7 +13161,7 @@ snapshots: node-forge: 1.4.0 npm-package-arg: 11.0.3 ora: 3.4.0 - picomatch: 4.0.4 + picomatch: 4.0.7 pretty-format: 29.7.0 progress: 2.0.3 prompts: 2.4.2 @@ -13344,7 +13175,7 @@ snapshots: terminal-link: 2.1.1 toqr: 0.1.1 wrap-ansi: 7.0.0 - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: 3.25.76 optionalDependencies: react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) @@ -13383,7 +13214,7 @@ snapshots: '@expo/router-server': 57.0.8(@expo/metro-runtime@57.0.14)(expo-constants@57.0.16(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6)))(expo-font@57.0.2(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6))(react@19.2.6))(expo-server@57.0.3)(expo@57.0.18)(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.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@expo/ws-tunnel': 2.0.0(ws@8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) '@expo/xcpretty': 4.4.4 '@react-native/dev-middleware': 0.86.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) accepts: 1.3.8 @@ -13407,7 +13238,7 @@ snapshots: node-forge: 1.4.0 npm-package-arg: 11.0.3 ora: 3.4.0 - picomatch: 4.0.4 + picomatch: 4.0.7 pretty-format: 29.7.0 progress: 2.0.3 prompts: 2.4.2 @@ -13421,7 +13252,7 @@ snapshots: terminal-link: 2.1.1 toqr: 0.1.1 wrap-ansi: 7.0.0 - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: 3.25.76 optionalDependencies: react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6) @@ -13619,8 +13450,8 @@ snapshots: '@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.2 + '@jridgewell/sourcemap-codec': 1.6.0 + browserslist: 4.29.0 chalk: 4.1.2 debug: 4.4.3 getenv: 2.0.0 @@ -13629,7 +13460,7 @@ snapshots: jsc-safe-url: 0.2.4 lightningcss: 1.33.0 picomatch: 4.0.7 - postcss: 8.5.15 + postcss: 8.5.28 resolve-from: 5.0.0 optionalDependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) @@ -13713,7 +13544,7 @@ snapshots: '@expo/plist@0.8.1': dependencies: - '@xmldom/xmldom': 0.8.13 + '@xmldom/xmldom': 0.8.15 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -13807,15 +13638,15 @@ snapshots: - '@types/react' - '@types/react-dom' - '@expo/ws-tunnel@2.0.0(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@expo/ws-tunnel@2.0.0(ws@8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6))': dependencies: - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@expo/xcpretty@4.4.4': dependencies: '@babel/code-frame': 7.29.7 chalk: 4.1.2 - js-yaml: 4.2.0 + js-yaml: 4.3.2 '@ff-labs/fff-bin-darwin-arm64@0.9.4': optional: true @@ -13858,34 +13689,51 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.11 + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + '@floating-ui/dom@1.7.6': dependencies: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + '@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.2.6 react-dom: 19.2.6(react@19.2.6) - '@floating-ui/react@0.27.19(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@floating-ui/react-dom@2.1.9(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) - '@floating-ui/utils': 0.2.11 + '@floating-ui/dom': 1.8.0 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + '@floating-ui/react@0.27.20(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + '@floating-ui/utils': 0.2.12 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - tabbable: 6.4.0 + tabbable: 6.5.0 '@floating-ui/utils@0.2.11': {} + '@floating-ui/utils@0.2.12': {} + '@formkit/auto-animate@0.9.0': {} '@gar/promise-retry@1.0.3': {} - '@hono/node-server@1.19.14(hono@4.12.27)': + '@hono/node-server@1.19.17(hono@4.13.7)': dependencies: - hono: 4.12.27 + hono: 4.13.7 '@hono/node-server@1.19.9(hono@4.11.4)': dependencies: @@ -14062,7 +13910,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.11.1 + '@emnapi/runtime': 1.11.3 optional: true '@img/sharp-wasm32@0.35.4': @@ -14150,7 +13998,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': @@ -14165,12 +14013,12 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 '@js-temporal/polyfill@0.5.1': dependencies: @@ -14224,7 +14072,7 @@ snapshots: '@lexical/extension@0.41.0': dependencies: '@lexical/utils': 0.41.0 - '@preact/signals-core': 1.14.2 + '@preact/signals-core': 1.14.4 lexical: 0.41.0 '@lexical/hashtag@0.41.0': @@ -14289,9 +14137,9 @@ snapshots: '@lexical/utils': 0.41.0 lexical: 0.41.0 - '@lexical/react@0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.31)': + '@lexical/react@0.41.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(yjs@13.6.32)': 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.20(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@lexical/devtools-core': 0.41.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@lexical/dragon': 0.41.0 '@lexical/extension': 0.41.0 @@ -14307,12 +14155,13 @@ snapshots: '@lexical/table': 0.41.0 '@lexical/text': 0.41.0 '@lexical/utils': 0.41.0 - '@lexical/yjs': 0.41.0(yjs@13.6.31) + '@lexical/yjs': 0.41.0(yjs@13.6.32) lexical: 0.41.0 react: 19.2.6 react-dom: 19.2.6(react@19.2.6) - react-error-boundary: 6.1.2(react@19.2.6) + react-error-boundary: 6.1.6(@types/react@19.2.16)(react@19.2.6) transitivePeerDependencies: + - '@types/react' - yjs '@lexical/rich-text@0.41.0': @@ -14343,12 +14192,12 @@ snapshots: '@lexical/selection': 0.41.0 lexical: 0.41.0 - '@lexical/yjs@0.41.0(yjs@13.6.31)': + '@lexical/yjs@0.41.0(yjs@13.6.32)': dependencies: '@lexical/offset': 0.41.0 '@lexical/selection': 0.41.0 lexical: 0.41.0 - yjs: 13.6.31 + yjs: 13.6.32 '@libsql/client@0.17.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)': dependencies: @@ -14423,9 +14272,9 @@ snapshots: '@material/material-color-utilities@0.3.0': {} - '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.6.5)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.27) + '@hono/node-server': 1.19.17(hono@4.13.7) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -14435,13 +14284,13 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.27 + hono: 4.13.7 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.4.3 - zod-to-json-schema: 3.25.2(zod@4.4.3) + zod: 4.6.5 + zod-to-json-schema: 3.25.2(zod@4.6.5) transitivePeerDependencies: - supports-color @@ -14534,25 +14383,32 @@ snapshots: '@napi-rs/keyring-win32-ia32-msvc': 1.3.0 '@napi-rs/keyring-win32-x64-msvc': 1.3.0 - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.4(@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 + '@tybys/wasm-util': 0.10.4 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.2.4(@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 + '@tybys/wasm-util': 0.10.4 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + '@napi-rs/wasm-runtime@1.2.4(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)': dependencies: '@emnapi/core': 1.11.2 - '@emnapi/runtime': 1.11.2 - '@tybys/wasm-util': 0.10.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.4 optional: true '@neon-rs/load@0.0.4': {} @@ -14569,6 +14425,8 @@ snapshots: '@nodable/entities@2.1.1': {} + '@nodable/entities@3.0.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -14579,7 +14437,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + fastq: 1.20.3 '@nornagon/put@0.0.8': {} @@ -14588,7 +14446,7 @@ snapshots: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.5.1 + lru-cache: 11.5.2 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -15037,7 +14895,7 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@preact/signals-core@1.14.2': {} + '@preact/signals-core@1.14.4': {} '@prisma/debug@7.2.0': {} @@ -15225,15 +15083,15 @@ snapshots: commander: 14.0.3 ignore: 7.0.5 ora: 9.4.0 - package-manager-detector: 1.6.0 + package-manager-detector: 1.8.0 picocolors: 1.1.1 prompts: 2.4.2 - tinyexec: 1.2.4 + tinyexec: 1.3.1 '@react-native-ai/apple@0.12.0(patch_hash=2d09870c2848d185cb05b53ed823a46e12dba519324d8dd8e584e28731990f9d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))': dependencies: - '@ai-sdk/provider': 3.0.16 - '@ai-sdk/provider-utils': 4.0.51(zod@4.4.3) + '@ai-sdk/provider': 3.0.15 + '@ai-sdk/provider-utils': 4.0.49(zod@4.4.3) react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) zod: 4.4.3 @@ -15402,7 +15260,7 @@ snapshots: '@react-navigation/routers': 7.6.4 escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.12 + nanoid: 3.3.19 query-string: 7.1.3 react: 19.2.3 react-is: 19.2.7 @@ -15421,7 +15279,7 @@ snapshots: optionalDependencies: '@react-native-masked-view/masked-view': 0.3.2(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) - '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(ad1eff2c3e588b799b6541240bb21d97)': + '@react-navigation/native-stack@7.17.6(patch_hash=e667c3cef8c78bb9ff4882ee5bd23a432247b843060a9499eb5f07e9e2295552)(f640afa6cd4b9a1dc5422cbafdc82f53)': dependencies: '@react-navigation/elements': 2.9.26(c10301b6e0c42fc6434d2b643197a81e) '@react-navigation/native': 7.3.4(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) @@ -15429,7 +15287,7 @@ snapshots: react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) react-native-safe-area-context: 5.7.0(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) - react-native-screens: 4.26.2(patch_hash=8156dd0f3407822404793cfdaa95639a36b62102f4507c981b8be83600bb382d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) + react-native-screens: 4.26.2(patch_hash=595114f3f9ce8b9f0d99d1aafe5c12c997b7d75ca9add31d46cbedbfe347b8e0)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: @@ -15440,7 +15298,7 @@ snapshots: '@react-navigation/core': 7.21.13(react@19.2.3) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.12 + nanoid: 3.3.19 react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) standard-navigation: 0.0.7 @@ -15448,7 +15306,7 @@ snapshots: '@react-navigation/routers@7.6.4': dependencies: - nanoid: 3.3.12 + nanoid: 3.3.19 '@redis/bloom@6.2.1(@redis/client@6.2.1)': dependencies: @@ -15549,7 +15407,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.2.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17': @@ -15564,7 +15422,7 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.2.5': optional: true - '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5)': + '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5)': dependencies: '@babel/core': 7.29.7 picomatch: 4.0.4 @@ -15572,96 +15430,13 @@ snapshots: optionalDependencies: '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7) '@babel/runtime': 7.29.7 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' '@rolldown/pluginutils@1.0.0-rc.17': optional: true '@rolldown/pluginutils@1.0.1': {} - '@rollup/pluginutils@5.4.0(rollup@4.61.0)': - dependencies: - '@types/estree': 1.0.9 - estree-walker: 2.0.2 - picomatch: 4.0.4 - optionalDependencies: - rollup: 4.61.0 - - '@rollup/rollup-android-arm-eabi@4.61.0': - optional: true - - '@rollup/rollup-android-arm64@4.61.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.61.0': - optional: true - - '@rollup/rollup-darwin-x64@4.61.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.61.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.61.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.61.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.61.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.61.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.61.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.61.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.61.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.61.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.61.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.61.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.61.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.61.0': - optional: true - '@shikijs/core@4.2.0': dependencies: '@shikijs/primitive': 4.2.0 @@ -15742,57 +15517,56 @@ snapshots: '@sindresorhus/is@4.6.0': {} - '@smithy/core@3.24.6': + '@smithy/core@3.34.1': dependencies: - '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.14.3 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@smithy/credential-provider-imds@4.3.8': dependencies: - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@smithy/fetch-http-handler@5.4.6': dependencies: - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/node-config-provider@4.4.6': + '@smithy/node-config-provider@4.6.2': dependencies: - '@smithy/core': 3.24.6 + '@smithy/core': 3.34.1 tslib: 2.8.1 '@smithy/node-http-handler@4.7.7': dependencies: - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 - '@smithy/shared-ini-file-loader@4.5.6': + '@smithy/shared-ini-file-loader@4.7.2': dependencies: - '@smithy/core': 3.24.6 + '@smithy/core': 3.34.1 tslib: 2.8.1 '@smithy/signature-v4@5.4.6': dependencies: - '@smithy/core': 3.24.6 - '@smithy/types': 4.14.3 + '@smithy/core': 3.34.1 + '@smithy/types': 4.18.0 tslib: 2.8.1 - '@smithy/types@4.14.3': + '@smithy/types@4.18.0': dependencies: tslib: 2.8.1 - '@smithy/util-base64@4.4.6': + '@smithy/util-base64@4.6.2': dependencies: - '@smithy/core': 3.24.6 + '@smithy/core': 3.34.1 tslib: 2.8.1 '@smithy/util-buffer-from@2.2.0': @@ -15847,7 +15621,7 @@ snapshots: '@tailwindcss/node@4.3.3': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.25.1 + enhanced-resolve: 5.24.5 jiti: 2.7.0 lightningcss: 1.33.0 magic-string: 0.30.21 @@ -15905,12 +15679,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 - '@tailwindcss/vite@4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' '@tanstack/devtools-event-client@0.4.3': {} @@ -15921,7 +15695,7 @@ snapshots: '@tanstack/devtools-event-client': 0.4.3 '@tanstack/store': 0.8.1 - '@tanstack/query-core@5.103.1': {} + '@tanstack/query-core@5.102.8': {} '@tanstack/react-pacer@0.19.4(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: @@ -15962,18 +15736,18 @@ snapshots: '@tanstack/router-generator@1.167.12': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@tanstack/router-core': 1.171.8 '@tanstack/router-utils': 1.162.1 '@tanstack/virtual-file-routes': 1.162.0 jiti: 2.7.0 magic-string: 0.30.21 prettier: 3.8.3 - zod: 4.4.3 + zod: 4.6.5 transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))': + '@tanstack/router-plugin@1.168.13(@tanstack/react-router@1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) @@ -15990,7 +15764,7 @@ snapshots: zod: 4.4.3 optionalDependencies: '@tanstack/react-router': 1.170.10(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' transitivePeerDependencies: - supports-color @@ -15999,7 +15773,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 ansis: 4.3.1 babel-dead-code-elimination: 1.0.12 diff: 8.0.3 @@ -16041,6 +15815,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aria-query@5.0.4': {} '@types/aws-lambda@8.10.161': {} @@ -16055,16 +15834,16 @@ snapshots: '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/body-parser@1.19.6': dependencies: @@ -16135,6 +15914,10 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + '@types/http-cache-semantics@4.2.0': {} '@types/http-errors@2.0.5': {} @@ -16312,37 +16095,37 @@ snapshots: optionalDependencies: ajv: 6.15.0 - '@vitejs/plugin-react@6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(babel-plugin-react-compiler@1.0.0)': + '@vitejs/plugin-react@6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5))(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(babel-plugin-react-compiler@1.0.0)': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' optionalDependencies: - '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5) + '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/plugin-transform-runtime@7.29.7(@babel/core@7.29.7))(@babel/runtime@7.29.7)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(rolldown@1.2.5) babel-plugin-react-compiler: 1.0.0 - '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11)': + '@vitest/browser-preview@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) - vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) + vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11)': + '@vitest/browser@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) '@vitest/utils': 4.1.11 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil - msw @@ -16358,14 +16141,14 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))': + '@vitest/mocker@4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))': dependencies: '@vitest/spy': 4.1.11 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.11(@types/node@24.12.4)(typescript@7.0.2) - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' '@vitest/pretty-format@4.1.11': dependencies: @@ -16391,12 +16174,12 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)': + '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)': dependencies: '@oxc-project/runtime': 0.146.0 '@oxc-project/types': 0.146.0 lightningcss: 1.33.0 - postcss: 8.5.15 + postcss: 8.5.28 yuku-codegen: 0.5.48 yuku-parser: 0.5.48 optionalDependencies: @@ -16409,7 +16192,7 @@ snapshots: '@voidzero-dev/vite-plus-linux-x64-musl': 0.3.0 '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.3.0 '@voidzero-dev/vite-plus-win32-x64-msvc': 0.3.0 - esbuild: 0.28.1 + esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 @@ -16417,12 +16200,12 @@ snapshots: unrun: 0.2.39 yaml: 2.9.0 - '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)': + '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)': dependencies: '@oxc-project/runtime': 0.146.0 '@oxc-project/types': 0.146.0 lightningcss: 1.33.0 - postcss: 8.5.15 + postcss: 8.5.28 yuku-codegen: 0.5.48 yuku-parser: 0.5.48 optionalDependencies: @@ -16435,7 +16218,7 @@ snapshots: '@voidzero-dev/vite-plus-linux-x64-musl': 0.3.0 '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.3.0 '@voidzero-dev/vite-plus-win32-x64-msvc': 0.3.0 - esbuild: 0.28.1 + esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 @@ -16517,9 +16300,9 @@ snapshots: '@vscode/l10n@0.0.18': {} - '@xmldom/xmldom@0.8.13': {} + '@xmldom/xmldom@0.8.15': {} - '@xmldom/xmldom@0.9.10': {} + '@xmldom/xmldom@0.9.12': {} '@yuku-codegen/binding-darwin-arm64@0.5.48': optional: true @@ -16687,17 +16470,17 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - alchemy@2.0.0-beta.76(8f001107992eb148f96e7a0a6f857223): + alchemy@2.0.0-beta.76(943b389e45c63e6617cf6f94957d1aa7): dependencies: - '@alchemy.run/cloudflare-runtime': 2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)))(@effect/platform-bun@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6))(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(@types/node@24.12.4)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(rolldown@1.2.5)(typescript@7.0.2) + '@alchemy.run/cloudflare-runtime': 2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)))(@effect/platform-bun@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6))(@effect/platform-node@4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6))(@types/node@24.12.4)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(rolldown@1.2.5)(typescript@7.0.2) '@alchemy.run/floci': 2.0.0-beta.76(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@alchemy.run/node-utils': 2.0.0-beta.76 '@aws-sdk/credential-providers': 3.1062.0 - '@clack/prompts': 1.7.0 + '@clack/prompts': 1.8.1 '@distilled.cloud/aws': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@distilled.cloud/axiom': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@distilled.cloud/cloudflare': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) @@ -16707,30 +16490,30 @@ snapshots: '@distilled.cloud/neon': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@distilled.cloud/planetscale': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@distilled.cloud/railway': 1.0.0-rc.8(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) - '@effect/sql-d1': 4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) - '@effect/sql-sqlite-do': 4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + '@effect/sql-d1': 4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + '@effect/sql-sqlite-do': 4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@effect/vitest': 4.0.0-rc.112(patch_hash=a16b1e870d8c29e4a98b17cc4c638a4ff471753d8ca3487f78a22b759caf951b)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@libsql/client': 0.17.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@octokit/rest': 22.0.1 '@octokit/webhooks': 14.2.0 '@prisma/dev': 0.20.0(typescript@7.0.2) - '@smithy/node-config-provider': 4.4.6 - '@smithy/shared-ini-file-loader': 4.5.6 - '@smithy/types': 4.14.3 + '@smithy/node-config-provider': 4.6.2 + '@smithy/shared-ini-file-loader': 4.7.2 + '@smithy/types': 4.18.0 '@types/aws-lambda': 8.10.161 aws4fetch: 1.0.20 capnweb: 0.6.1 effect: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) fast-glob: 3.3.3 - fast-xml-parser: 5.8.0 + fast-xml-parser: 5.11.1 ink: 6.8.0(@types/react@19.2.16)(bufferutil@4.1.0)(react-devtools-core@6.1.5(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react@19.2.6)(utf-8-validate@6.0.6) jszip: 3.10.1 libsodium-wrappers: 0.8.4 pathe: 2.0.3 - picomatch: 4.0.4 + picomatch: 4.0.7 react: 19.2.6 rolldown: 1.2.5 - undici: 7.27.1 + undici: 7.29.1 yaml: 2.9.0 optionalDependencies: '@effect/platform-bun': 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(utf-8-validate@6.0.6) @@ -16738,11 +16521,11 @@ snapshots: '@effect/sql-mysql2': 4.0.0-rc.112(@types/node@24.12.4)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@effect/sql-pg': 4.0.0-rc.112(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) drizzle-kit: 1.0.0-rc.5-ab785fc - drizzle-orm: 1.0.0-rc.5-ab785fc(7dbc7f7255b0125741da736693a2e1eb) + drizzle-orm: 1.0.0-rc.5-ab785fc(cbff702a876b7c3154959f590880ea77) mongodb: 6.21.0(@aws-sdk/credential-providers@3.1062.0)(socks@2.8.9) mysql2: 3.24.4(@types/node@24.12.4) pg: 8.23.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - '@types/node' @@ -16794,6 +16577,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + anynum@1.0.1: {} + app-builder-lib@26.15.6(patch_hash=0fc9a327982b3fdd5d9f4946e17bd8bf8d3d5c9b3753f77f0b18f7c0f0bdb4db)(dmg-builder@26.15.6)(electron-builder-squirrel-windows@26.15.6): dependencies: '@electron/asar': 3.4.1 @@ -16825,7 +16610,7 @@ snapshots: hosted-git-info: 4.1.0 isbinaryfile: 5.0.7 jiti: 2.7.0 - js-yaml: 4.2.0 + js-yaml: 4.3.2 json5: 2.2.3 lazy-val: 1.0.5 minimatch: 10.2.5 @@ -16858,9 +16643,6 @@ snapshots: aria-query@5.3.2: {} - array-iterate@2.0.1: - optional: true - asap@2.0.6: {} asn1js@3.0.10: @@ -16871,67 +16653,63 @@ snapshots: assertion-error@2.0.1: {} - astro@7.0.3(@astrojs/markdown-remark@7.2.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(aws4fetch@1.0.20)(idb-keyval@6.2.1)(ioredis@5.11.0)(jiti@2.7.0)(rollup@4.61.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0): + astro@7.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3)(@types/node@24.12.4)(aws4fetch@1.0.20)(idb-keyval@6.2.1)(ioredis@5.11.0)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0): dependencies: - '@astrojs/compiler-rs': 0.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - '@astrojs/internal-helpers': 0.10.0 - '@astrojs/markdown-satteri': 0.3.2 - '@astrojs/telemetry': 3.3.2 + '@astrojs/compiler-rs': 0.4.1(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.3) + '@astrojs/internal-helpers': 0.10.4 + '@astrojs/markdown-satteri': 0.3.8 + '@astrojs/telemetry': 3.3.3 '@capsizecss/unpack': 4.0.1 - '@clack/prompts': 1.6.0 + '@clack/prompts': 1.8.1 '@oslojs/encoding': 1.1.0 - '@rollup/pluginutils': 5.4.0(rollup@4.61.0) am-i-vibing: 0.4.0 aria-query: 5.3.2 axobject-query: 4.1.0 ci-info: 4.4.0 clsx: 2.1.1 common-ancestor-path: 2.0.0 - cookie: 1.1.1 - devalue: 5.8.1 - diff: 8.0.3 + cookie: 2.0.1 + devalue: 5.9.2 + diff: 9.0.0 dset: 3.1.4 - es-module-lexer: 2.1.0 - esbuild: 0.28.1 + es-module-lexer: 2.3.2 + esbuild: 0.28.2 + find-proc: 0.1.0 flattie: 1.1.1 fontace: 0.4.1 get-tsconfig: 5.0.0-beta.4 github-slugger: 2.0.0 html-escaper: 3.0.3 http-cache-semantics: 4.2.0 - js-yaml: 4.2.0 + js-yaml: 4.3.2 jsonc-parser: 3.3.1 - magic-string: 0.30.21 - magicast: 0.5.3 + magic-string: 1.4.1 + magicast: 0.5.5 mrmime: 2.0.1 - neotraverse: 0.6.18 - obug: 2.1.3 - p-limit: 7.3.0 - p-queue: 9.3.0 - package-manager-detector: 1.6.0 + neotraverse: 1.0.1 + obug: 2.2.1 + p-limit: 7.3.2 + p-queue: 9.3.3 + package-manager-detector: 1.8.0 piccolore: 0.1.3 - picomatch: 4.0.4 - rehype: 13.0.2 + picomatch: 4.0.7 semver: 7.8.5 shiki: 4.2.0 - smol-toml: 1.7.0 - svgo: 4.0.1 + smol-toml: 1.8.0 + svgo: 4.1.0 tinyclip: 0.1.15 - tinyexec: 1.2.4 + tinyexec: 1.3.1 tinyglobby: 0.2.17 - ultrahtml: 1.6.0 - unifont: 0.7.4 - unist-util-visit: 5.1.0 + ultrahtml: 1.7.0 + unifont: 0.7.5 unstorage: 1.17.5(aws4fetch@1.0.20)(idb-keyval@6.2.1)(ioredis@5.11.0) - vfile: 6.0.3 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' - vitefu: 1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)) + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' + vitefu: 1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 - zod: 4.4.3 + zod: 4.6.5 optionalDependencies: - '@astrojs/markdown-remark': 7.2.0 - sharp: 0.34.5 + sharp: 0.35.4(@types/node@24.12.4) transitivePeerDependencies: - '@arethetypeswrong/core' - '@azure/app-configuration' @@ -16959,7 +16737,6 @@ snapshots: - jiti - less - publint - - rollup - sass - sass-embedded - stylus @@ -16999,7 +16776,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/parser': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -17118,7 +16895,7 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.10.33: {} + baseline-browser-mapping@2.11.24: {} before-after-hook@4.0.0: {} @@ -17138,7 +16915,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.3 + qs: 6.16.0 raw-body: 3.0.2 type-is: 2.1.0 transitivePeerDependencies: @@ -17163,16 +16940,16 @@ snapshots: dependencies: big-integer: 1.6.52 - brace-expansion@1.1.15: + brace-expansion@1.1.21: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.1: + brace-expansion@2.1.7: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.6: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 @@ -17184,13 +16961,13 @@ snapshots: dependencies: lodash: 4.18.1 - browserslist@4.28.2: + browserslist@4.29.0: dependencies: - baseline-browser-mapping: 2.10.33 - caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.364 - node-releases: 2.0.46 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + baseline-browser-mapping: 2.11.24 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.430 + node-releases: 2.0.55 + update-browserslist-db: 1.3.3(browserslist@4.29.0) bser@2.1.1: dependencies: @@ -17206,17 +16983,10 @@ snapshots: node-gyp-build: 4.8.4 optional: true - builder-util-runtime@9.5.1: - dependencies: - debug: 4.4.3 - sax: 1.6.0 - transitivePeerDependencies: - - supports-color - builder-util-runtime@9.7.0: dependencies: debug: 4.4.3 - sax: 1.6.0 + sax: 1.6.1 transitivePeerDependencies: - supports-color @@ -17230,7 +17000,7 @@ snapshots: fs-extra: 10.1.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - js-yaml: 4.2.0 + js-yaml: 4.3.2 sanitize-filename: 1.6.4 source-map-support: 0.5.21 stat-mode: 1.0.0 @@ -17252,7 +17022,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.6 - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 minipass-collect: 2.0.1 minipass-flush: 1.0.7 @@ -17286,7 +17056,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001793: {} + caniuse-lite@1.0.30001810: {} capnp-es@0.0.14(typescript@7.0.2): optionalDependencies: @@ -17531,11 +17301,14 @@ snapshots: cookie@0.7.2: {} - cookie@1.1.1: {} + cookie@1.1.1: + optional: true + + cookie@2.0.1: {} core-js-compat@3.49.0: dependencies: - browserslist: 4.28.2 + browserslist: 4.29.0 core-js@3.47.0: {} @@ -17574,6 +17347,14 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 + css-select@6.0.0: + dependencies: + boolbase: 1.0.0 + css-what: 7.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + css-tree@1.1.3: dependencies: mdn-data: 2.0.14 @@ -17591,6 +17372,8 @@ snapshots: css-what@6.2.2: {} + css-what@7.0.0: {} + csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -17599,6 +17382,9 @@ snapshots: culori@4.0.2: {} + date-fns@4.4.0: + optional: true + dbus-next@0.10.2(patch_hash=cfff57561b0ee59b5addb3b2e6c6f20906e967507a530ab67e8db8108e520ba4): dependencies: '@nornagon/put': 0.0.8 @@ -17681,7 +17467,7 @@ snapshots: detect-node@2.1.0: optional: true - devalue@5.8.1: {} + devalue@5.9.2: {} devlop@1.1.0: dependencies: @@ -17701,7 +17487,7 @@ snapshots: app-builder-lib: 26.15.6(patch_hash=0fc9a327982b3fdd5d9f4946e17bd8bf8d3d5c9b3753f77f0b18f7c0f0bdb4db)(dmg-builder@26.15.6)(electron-builder-squirrel-windows@26.15.6) builder-util: 26.15.3 fs-extra: 10.1.0 - js-yaml: 4.2.0 + js-yaml: 4.3.2 transitivePeerDependencies: - electron-builder-squirrel-windows - supports-color @@ -17743,17 +17529,17 @@ snapshots: '@drizzle-team/brocli': 0.12.0 '@js-temporal/polyfill': 0.5.1 esbuild: 0.25.12 - get-tsconfig: 4.14.0 + get-tsconfig: 4.14.3 jiti: 2.7.0 - drizzle-orm@1.0.0-rc.5-ab785fc(7dbc7f7255b0125741da736693a2e1eb): + drizzle-orm@1.0.0-rc.5-ab785fc(cbff702a876b7c3154959f590880ea77): optionalDependencies: '@cloudflare/workers-types': 4.20260604.1 - '@effect/sql-d1': 4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + '@effect/sql-d1': 4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@effect/sql-mysql2': 4.0.0-rc.112(@types/node@24.12.4)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@effect/sql-pg': 4.0.0-rc.112(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@effect/sql-sqlite-bun': 4.0.0-rc.112(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) - '@effect/sql-sqlite-do': 4.0.0-rc.116(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) + '@effect/sql-sqlite-do': 4.0.0-rc.115(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2)) '@electric-sql/pglite': 0.3.15 '@libsql/client': 0.17.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) bun-types: 1.3.14 @@ -17782,7 +17568,7 @@ snapshots: effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2): dependencies: - fast-check: 4.9.0 + fast-check: 4.10.2 msgpackr: 2.1.0 ejs@3.1.10: @@ -17821,7 +17607,7 @@ snapshots: builder-util: 26.15.3 builder-util-runtime: 9.7.0 chalk: 4.1.2 - form-data: 4.0.5 + form-data: 4.0.6 fs-extra: 10.1.0 lazy-val: 1.0.5 mime: 2.6.0 @@ -17833,13 +17619,13 @@ snapshots: conf: 10.2.0 type-fest: 2.19.0 - electron-to-chromium@1.5.364: {} + electron-to-chromium@1.5.430: {} - electron-updater@6.8.3: + electron-updater@6.8.9: dependencies: - builder-util-runtime: 9.5.1 + builder-util-runtime: 9.7.0 fs-extra: 10.1.0 - js-yaml: 4.2.0 + js-yaml: 4.3.2 lazy-val: 1.0.5 lodash.escaperegexp: 4.1.2 lodash.isequal: 4.5.0 @@ -17868,6 +17654,14 @@ snapshots: transitivePeerDependencies: - supports-color + electron@44.4.2: + dependencies: + '@electron-internal/extract-zip': 1.0.5 + '@electron/get': 5.1.0 + '@types/node': 24.12.4 + transitivePeerDependencies: + - supports-color + emmet@2.4.11: dependencies: '@emmetio/abbreviation': 2.3.3 @@ -17881,11 +17675,16 @@ snapshots: encodeurl@2.0.0: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + end-of-stream@1.4.5: dependencies: once: 1.4.0 - enhanced-resolve@5.25.1: + enhanced-resolve@5.24.5: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -17910,7 +17709,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.1.0: {} + es-module-lexer@2.3.2: {} es-object-atoms@1.1.2: dependencies: @@ -17923,7 +17722,7 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.4 - es-toolkit@1.47.0: {} + es-toolkit@1.52.0: {} es6-error@4.1.1: optional: true @@ -17959,34 +17758,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.28.1: + esbuild@0.28.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 escalade@3.2.0: {} @@ -18002,8 +17801,6 @@ snapshots: estree-util-is-identifier-name@3.0.0: {} - estree-walker@2.0.2: {} - estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -18163,7 +17960,7 @@ snapshots: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) ua-parser-js: 0.7.41 - expo-document-picker@57.0.2(expo@57.0.18): + expo-document-picker@57.0.1(expo@57.0.18): dependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) @@ -18209,7 +18006,7 @@ snapshots: dependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) - expo-image-manipulator@57.0.19(expo@57.0.18): + expo-image-manipulator@57.0.17(expo@57.0.18): dependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) expo-image-loader: 57.0.1(expo@57.0.18) @@ -18228,12 +18025,12 @@ snapshots: expo-json-utils@57.0.1: {} - expo-keep-awake@57.0.1(expo@57.0.18)(react@19.2.3): + expo-keep-awake@57.0.2(expo@57.0.18)(react@19.2.3): dependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) react: 19.2.3 - expo-keep-awake@57.0.1(expo@57.0.18)(react@19.2.6): + expo-keep-awake@57.0.2(expo@57.0.18)(react@19.2.6): dependencies: expo: 57.0.18(0bf40d5ad6281d8f1e92517b24812edb) react: 19.2.6 @@ -18412,7 +18209,7 @@ snapshots: transitivePeerDependencies: - supports-color - expo-video@57.0.4(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): + expo-video@57.0.3(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): dependencies: expo: 57.0.18(fc5a731e35a0144aab60c7305f29cbed) react: 19.2.3 @@ -18455,7 +18252,7 @@ snapshots: expo-constants: 57.0.16(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6)) expo-file-system: 57.0.6(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6)) expo-font: 57.0.2(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6))(react@19.2.6) - expo-keep-awake: 57.0.1(expo@57.0.18)(react@19.2.6) + expo-keep-awake: 57.0.2(expo@57.0.18)(react@19.2.6) expo-modules-autolinking: 57.0.12(typescript@7.0.2) expo-modules-core: 57.0.14(react-native-worklets@0.10.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6))(react@19.2.6))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.6)(utf-8-validate@6.0.6))(react@19.2.6) pretty-format: 29.7.0 @@ -18498,7 +18295,7 @@ snapshots: expo-constants: 57.0.16(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) expo-file-system: 57.0.6(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6)) expo-font: 57.0.2(expo@57.0.18)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) - expo-keep-awake: 57.0.1(expo@57.0.18)(react@19.2.3) + expo-keep-awake: 57.0.2(expo@57.0.18)(react@19.2.3) expo-modules-autolinking: 57.0.12(typescript@7.0.2) expo-modules-core: 57.0.14(react-native-worklets@0.10.1(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3) pretty-format: 29.7.0 @@ -18527,7 +18324,7 @@ snapshots: express-rate-limit@8.5.2(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.2.0 + ip-address: 10.7.2 express@5.2.1: dependencies: @@ -18551,7 +18348,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.3 + qs: 6.16.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -18564,9 +18361,9 @@ snapshots: extend@3.0.2: {} - fast-check@4.9.0: + fast-check@4.10.2: dependencies: - pure-rand: 8.4.0 + pure-rand: 8.4.2 fast-deep-equal@3.1.3: {} @@ -18591,7 +18388,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.2: {} + fast-uri@3.1.8: {} fast-wrap-ansi@0.2.2: dependencies: @@ -18599,27 +18396,28 @@ snapshots: fast-xml-builder@1.2.0: dependencies: - path-expression-matcher: 1.5.0 + path-expression-matcher: 1.6.2 xml-naming: 0.1.0 - fast-xml-parser@5.7.3: + fast-xml-parser@5.11.1: dependencies: - '@nodable/entities': 2.1.1 + '@nodable/entities': 3.0.0 fast-xml-builder: 1.2.0 - path-expression-matcher: 1.5.0 - strnum: 2.3.0 + is-unsafe: 2.0.2 + path-expression-matcher: 1.6.2 + strnum: 2.4.2 + xml-naming: 0.3.0 - fast-xml-parser@5.8.0: + fast-xml-parser@5.7.3: dependencies: '@nodable/entities': 2.1.1 fast-xml-builder: 1.2.0 - path-expression-matcher: 1.5.0 - strnum: 2.3.0 - xml-naming: 0.1.0 + path-expression-matcher: 1.6.2 + strnum: 2.4.2 fastest-levenshtein@1.0.16: {} - fastq@1.20.1: + fastq@1.20.3: dependencies: reusify: 1.1.0 @@ -18633,10 +18431,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -18690,6 +18484,8 @@ snapshots: transitivePeerDependencies: - supports-color + find-proc@0.1.0: {} + find-up@3.0.0: dependencies: locate-path: 3.0.0 @@ -18713,7 +18509,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.5: + form-data@4.0.6: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -18721,7 +18517,7 @@ snapshots: hasown: 2.0.4 mime-types: 2.1.35 - formatly@0.7.1: + formatly@0.7.0: dependencies: fd-package-json: 2.0.0 package-manager-detector: 1.8.0 @@ -18818,10 +18614,6 @@ snapshots: dependencies: pump: 3.0.4 - get-tsconfig@4.14.0: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.14.3: dependencies: resolve-pkg-maps: 1.0.0 @@ -18903,7 +18695,7 @@ snapshots: defu: 6.1.7 destr: 2.0.5 iron-webcrypto: 1.2.1 - node-mock-http: 1.0.4 + node-mock-http: 1.0.5 radix3: 1.1.2 ufo: 1.6.4 uncrypto: 0.1.3 @@ -18927,15 +18719,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-from-html@2.0.3: - dependencies: - '@types/hast': 3.0.4 - devlop: 1.1.0 - hast-util-from-parse5: 8.0.3 - parse5: 7.3.0 - vfile: 6.0.3 - vfile-message: 4.0.3 - hast-util-from-parse5@8.0.3: dependencies: '@types/hast': 3.0.4 @@ -18947,14 +18730,9 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 - hast-util-is-element@3.0.0: - dependencies: - '@types/hast': 3.0.4 - optional: true - hast-util-parse-selector@4.0.0: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 hast-util-raw@9.1.0: dependencies: @@ -19022,21 +18800,13 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 - hast-util-to-text@4.0.2: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - hast-util-is-element: 3.0.0 - unist-util-find-after: 5.0.0 - optional: true - hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 hastscript@9.0.1: dependencies: - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 comma-separated-tokens: 2.0.3 hast-util-parse-selector: 4.0.0 property-information: 7.2.0 @@ -19075,7 +18845,7 @@ snapshots: hono@4.11.4: {} - hono@4.12.27: {} + hono@4.13.7: {} hosted-git-info@4.1.0: dependencies: @@ -19124,6 +18894,11 @@ snapshots: transitivePeerDependencies: - supports-color + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -19165,7 +18940,7 @@ snapshots: cli-cursor: 4.0.0 cli-truncate: 5.2.0 code-excerpt: 4.0.0 - es-toolkit: 1.47.0 + es-toolkit: 1.52.0 indent-string: 5.0.0 is-in-ci: 2.0.0 patch-console: 2.0.0 @@ -19180,7 +18955,7 @@ snapshots: type-fest: 5.7.0 widest-line: 6.0.0 wrap-ansi: 9.0.2 - ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + ws: 8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) yoga-layout: 3.2.1 optionalDependencies: '@types/react': 19.2.16 @@ -19208,7 +18983,7 @@ snapshots: - supports-color optional: true - ip-address@10.2.0: {} + ip-address@10.7.2: {} ipaddr.js@1.9.1: {} @@ -19231,8 +19006,6 @@ snapshots: is-docker@2.2.1: {} - is-docker@3.0.0: {} - is-docker@4.0.0: {} is-extglob@2.1.1: {} @@ -19251,10 +19024,6 @@ snapshots: is-in-ci@2.0.0: {} - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - is-interactive@2.0.0: {} is-node-process@1.2.0: @@ -19272,14 +19041,12 @@ snapshots: is-unicode-supported@2.1.0: {} + is-unsafe@2.0.2: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 - is-wsl@3.1.1: - dependencies: - is-inside-container: 1.0.0 - isarray@1.0.0: {} isbinaryfile@4.0.10: {} @@ -19343,7 +19110,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.2.0: + js-yaml@4.3.2: dependencies: argparse: 2.0.1 @@ -19412,7 +19179,7 @@ snapshots: knip@6.34.0: dependencies: fdir: 6.5.0(picomatch@4.0.7) - formatly: 0.7.1 + formatly: 0.7.0 get-tsconfig: 4.14.3 jiti: 2.7.0 oxc-parser: 0.147.0 @@ -19560,7 +19327,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.5.1: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: @@ -19570,7 +19337,7 @@ snapshots: dependencies: yallist: 4.0.0 - lru.min@1.1.4: {} + lru.min@1.1.5: {} lru_map@0.4.1: {} @@ -19582,12 +19349,16 @@ snapshots: magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 - magicast@0.5.3: + magic-string@1.4.1: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 source-map-js: 1.2.1 make-fetch-happen@15.0.6: @@ -19624,13 +19395,6 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-definitions@6.0.0: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - unist-util-visit: 5.1.0 - optional: true - mdast-util-directive@3.1.0: dependencies: '@types/mdast': 4.0.4 @@ -19969,7 +19733,7 @@ snapshots: metro-source-map@0.84.4: dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.84.4 @@ -19983,7 +19747,7 @@ snapshots: metro-source-map@0.84.5: dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 invariant: 2.2.4 metro-symbolicate: 0.84.5 @@ -20042,8 +19806,8 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 metro: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) metro-babel-transformer: 0.84.4 @@ -20062,8 +19826,8 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 flow-enums-runtime: 0.0.6 metro: 0.84.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) metro-babel-transformer: 0.84.5 @@ -20086,7 +19850,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 accepts: 2.0.0 ci-info: 2.0.0 connect: 3.7.0 @@ -20129,10 +19893,10 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/core': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 accepts: 2.0.0 ci-info: 2.0.0 connect: 3.7.0 @@ -20407,19 +20171,19 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.12 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.15 + brace-expansion: 1.1.21 minimatch@5.1.9: dependencies: - brace-expansion: 2.1.1 + brace-expansion: 2.1.7 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.1 + brace-expansion: 2.1.7 minimist@1.2.8: {} @@ -20542,15 +20306,15 @@ snapshots: generate-function: 2.3.1 iconv-lite: 0.7.3 long: 5.3.2 - lru.min: 1.1.4 + lru.min: 1.1.5 named-placeholders: 1.1.6 - sql-escaper: 1.5.1 + sql-escaper: 1.5.2 named-placeholders@1.1.6: dependencies: - lru.min: 1.1.4 + lru.min: 1.1.5 - nanoid@3.3.12: {} + nanoid@3.3.19: {} negotiator@0.6.3: {} @@ -20558,7 +20322,7 @@ snapshots: negotiator@1.0.0: {} - neotraverse@0.6.18: {} + neotraverse@1.0.1: {} nlcst-to-string@4.0.0: dependencies: @@ -20580,9 +20344,11 @@ snapshots: node-fetch-native@1.6.7: {} - node-fetch@2.7.0: + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 node-forge@1.4.0: {} @@ -20608,7 +20374,7 @@ snapshots: node-int64@0.4.0: {} - node-mock-http@1.0.4: {} + node-mock-http@1.0.5: {} node-pty@1.1.0: dependencies: @@ -20618,7 +20384,7 @@ snapshots: dependencies: es6-promise: 3.3.1 - node-releases@2.0.46: {} + node-releases@2.0.55: {} nopt@9.0.0: dependencies: @@ -20689,7 +20455,7 @@ snapshots: obuf@1.1.2: {} - obug@2.1.3: {} + obug@2.2.1: {} ofetch@1.5.1: dependencies: @@ -20697,7 +20463,7 @@ snapshots: node-fetch-native: 1.6.7 ufo: 1.6.4 - ohash@2.0.11: {} + ohash@2.0.12: {} on-finished@2.3.0: dependencies: @@ -20807,7 +20573,7 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.64.0(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)): + oxfmt@0.64.0(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)): dependencies: tinypool: 2.1.0 optionalDependencies: @@ -20830,7 +20596,7 @@ snapshots: '@oxfmt/binding-win32-arm64-msvc': 0.64.0 '@oxfmt/binding-win32-ia32-msvc': 0.64.0 '@oxfmt/binding-win32-x64-msvc': 0.64.0 - vite-plus: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) oxlint-tsgolint@7.0.2001: optionalDependencies: @@ -20841,7 +20607,7 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 7.0.2001 '@oxlint-tsgolint/win32-x64': 7.0.2001 - oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)): + oxlint@1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.79.0 '@oxlint/binding-android-arm64': 1.79.0 @@ -20863,7 +20629,7 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.79.0 '@oxlint/binding-win32-x64-msvc': 1.79.0 oxlint-tsgolint: 7.0.2001 - vite-plus: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) + vite-plus: 0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0) p-cancelable@2.1.1: {} @@ -20875,7 +20641,7 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@7.3.0: + p-limit@7.3.2: dependencies: yocto-queue: 1.2.2 @@ -20885,17 +20651,15 @@ snapshots: p-map@7.0.8: {} - p-queue@9.3.0: + p-queue@9.3.3: dependencies: eventemitter3: 5.0.4 - p-timeout: 7.0.1 + p-timeout: 7.0.2 - p-timeout@7.0.1: {} + p-timeout@7.0.2: {} p-try@2.2.0: {} - package-manager-detector@1.6.0: {} - package-manager-detector@1.8.0: {} pako@1.0.11: {} @@ -20910,16 +20674,6 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 - parse-latin@7.0.0: - dependencies: - '@types/nlcst': 2.0.3 - '@types/unist': 3.0.3 - nlcst-to-string: 4.0.0 - unist-util-modify-children: 4.0.0 - unist-util-visit-children: 3.0.0 - vfile: 6.0.3 - optional: true - parse-png@2.1.0: dependencies: pngjs: 3.4.0 @@ -20936,7 +20690,7 @@ snapshots: path-exists@3.0.0: {} - path-expression-matcher@1.5.0: {} + path-expression-matcher@1.6.2: {} path-is-absolute@1.0.1: {} @@ -20946,7 +20700,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 path-to-regexp@6.1.0: {} @@ -21045,13 +20799,13 @@ snapshots: plist@3.1.0: dependencies: - '@xmldom/xmldom': 0.8.13 + '@xmldom/xmldom': 0.8.15 base64-js: 1.5.1 xmlbuilder: 15.1.1 plist@3.1.1: dependencies: - '@xmldom/xmldom': 0.9.10 + '@xmldom/xmldom': 0.9.12 base64-js: 1.5.1 xmlbuilder: 15.1.1 @@ -21059,9 +20813,9 @@ snapshots: pngjs@7.0.0: {} - postcss@8.5.15: + postcss@8.5.28: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -21165,7 +20919,7 @@ snapshots: punycode@2.3.1: optional: true - pure-rand@8.4.0: {} + pure-rand@8.4.2: {} pvtsutils@1.3.6: dependencies: @@ -21173,7 +20927,7 @@ snapshots: pvutils@1.1.5: {} - qs@6.15.3: + qs@6.16.0: dependencies: es-define-property: 1.0.1 side-channel: 1.1.1 @@ -21206,7 +20960,7 @@ snapshots: react-devtools-core@6.1.5(bufferutil@4.1.0)(utf-8-validate@6.0.6): dependencies: - shell-quote: 1.8.4 + shell-quote: 1.10.0 ws: 7.5.11(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil @@ -21222,9 +20976,11 @@ snapshots: react: 19.2.6 scheduler: 0.27.0 - react-error-boundary@6.1.2(react@19.2.6): + react-error-boundary@6.1.6(@types/react@19.2.16)(react@19.2.6): dependencies: react: 19.2.6 + optionalDependencies: + '@types/react': 19.2.16 react-freeze@1.0.4(react@19.2.3): dependencies: @@ -21315,7 +21071,7 @@ snapshots: react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6) - react-native-screens@4.26.2(patch_hash=8156dd0f3407822404793cfdaa95639a36b62102f4507c981b8be83600bb382d)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): + react-native-screens@4.26.2(patch_hash=595114f3f9ce8b9f0d99d1aafe5c12c997b7d75ca9add31d46cbedbfe347b8e0)(react-native@0.86.3(@babel/core@7.29.7)(@react-native/metro-config@0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@types/react@19.2.16)(bufferutil@4.1.0)(react@19.2.3)(utf-8-validate@6.0.6))(react@19.2.3): dependencies: react: 19.2.3 react-freeze: 1.0.4(react@19.2.3) @@ -21368,7 +21124,7 @@ snapshots: '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@react-native/metro-config': 0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6) convert-source-map: 2.0.0 react: 19.2.3 @@ -21389,7 +21145,7 @@ snapshots: '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@react-native/metro-config': 0.86.3(@babel/core@7.29.7)(bufferutil@4.1.0)(utf-8-validate@6.0.6) convert-source-map: 2.0.0 react: 19.2.6 @@ -21610,12 +21366,6 @@ snapshots: dependencies: jsesc: 3.1.0 - rehype-parse@9.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-from-html: 2.0.3 - unified: 11.0.5 - rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -21627,19 +21377,6 @@ snapshots: '@types/hast': 3.0.4 hast-util-sanitize: 5.0.2 - rehype-stringify@10.0.1: - dependencies: - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - unified: 11.0.5 - - rehype@13.0.2: - dependencies: - '@types/hast': 3.0.4 - rehype-parse: 9.0.1 - rehype-stringify: 10.0.1 - unified: 11.0.5 - remark-breaks@4.0.0: dependencies: '@types/mdast': 4.0.4 @@ -21674,14 +21411,6 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - remark-smartypants@3.0.2: - dependencies: - retext: 9.0.0 - retext-smartypants: 6.2.0 - unified: 11.0.5 - unist-util-visit: 5.1.0 - optional: true - remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -21738,34 +21467,12 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retext-latin@4.0.0: - dependencies: - '@types/nlcst': 2.0.3 - parse-latin: 7.0.0 - unified: 11.0.5 - optional: true - retext-smartypants@6.2.0: dependencies: '@types/nlcst': 2.0.3 nlcst-to-string: 4.0.0 unist-util-visit: 5.1.0 - retext-stringify@4.0.0: - dependencies: - '@types/nlcst': 2.0.3 - nlcst-to-string: 4.0.0 - unified: 11.0.5 - optional: true - - retext@9.0.0: - dependencies: - '@types/nlcst': 2.0.3 - retext-latin: 4.0.0 - retext-stringify: 4.0.0 - unified: 11.0.5 - optional: true - retry@0.12.0: {} rettime@0.10.1: @@ -21830,38 +21537,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.5 '@rolldown/binding-win32-x64-msvc': 1.2.5 - rollup@4.61.0: - dependencies: - '@types/estree': 1.0.9 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.61.0 - '@rollup/rollup-android-arm64': 4.61.0 - '@rollup/rollup-darwin-arm64': 4.61.0 - '@rollup/rollup-darwin-x64': 4.61.0 - '@rollup/rollup-freebsd-arm64': 4.61.0 - '@rollup/rollup-freebsd-x64': 4.61.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.61.0 - '@rollup/rollup-linux-arm-musleabihf': 4.61.0 - '@rollup/rollup-linux-arm64-gnu': 4.61.0 - '@rollup/rollup-linux-arm64-musl': 4.61.0 - '@rollup/rollup-linux-loong64-gnu': 4.61.0 - '@rollup/rollup-linux-loong64-musl': 4.61.0 - '@rollup/rollup-linux-ppc64-gnu': 4.61.0 - '@rollup/rollup-linux-ppc64-musl': 4.61.0 - '@rollup/rollup-linux-riscv64-gnu': 4.61.0 - '@rollup/rollup-linux-riscv64-musl': 4.61.0 - '@rollup/rollup-linux-s390x-gnu': 4.61.0 - '@rollup/rollup-linux-x64-gnu': 4.61.0 - '@rollup/rollup-linux-x64-musl': 4.61.0 - '@rollup/rollup-openbsd-x64': 4.61.0 - '@rollup/rollup-openharmony-arm64': 4.61.0 - '@rollup/rollup-win32-arm64-msvc': 4.61.0 - '@rollup/rollup-win32-ia32-msvc': 4.61.0 - '@rollup/rollup-win32-x64-gnu': 4.61.0 - '@rollup/rollup-win32-x64-msvc': 4.61.0 - fsevents: 2.3.3 - optional: true - router@2.2.0: dependencies: debug: 4.4.3 @@ -21888,24 +21563,24 @@ snapshots: dependencies: truncate-utf8-bytes: 1.0.2 - satteri@0.9.3: + satteri@0.10.5: dependencies: '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 + '@types/hast': 3.0.5 '@types/mdast': 4.0.4 '@types/unist': 3.0.3 optionalDependencies: - '@bruits/satteri-darwin-arm64': 0.9.3 - '@bruits/satteri-darwin-x64': 0.9.3 - '@bruits/satteri-linux-arm64-gnu': 0.9.3 - '@bruits/satteri-linux-arm64-musl': 0.9.3 - '@bruits/satteri-linux-x64-gnu': 0.9.3 - '@bruits/satteri-linux-x64-musl': 0.9.3 - '@bruits/satteri-wasm32-wasi': 0.9.3 - '@bruits/satteri-win32-arm64-msvc': 0.9.3 - '@bruits/satteri-win32-x64-msvc': 0.9.3 - - sax@1.6.0: {} + '@bruits/satteri-darwin-arm64': 0.10.5 + '@bruits/satteri-darwin-x64': 0.10.5 + '@bruits/satteri-linux-arm64-gnu': 0.10.5 + '@bruits/satteri-linux-arm64-musl': 0.10.5 + '@bruits/satteri-linux-x64-gnu': 0.10.5 + '@bruits/satteri-linux-x64-musl': 0.10.5 + '@bruits/satteri-wasm32-wasi': 0.10.5 + '@bruits/satteri-win32-arm64-msvc': 0.10.5 + '@bruits/satteri-win32-x64-msvc': 0.10.5 + + sax@1.6.1: {} scheduler@0.27.0: {} @@ -22068,7 +21743,7 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.4: {} + shell-quote@1.10.0: {} shiki@4.2.0: dependencies: @@ -22172,8 +21847,6 @@ snapshots: smart-buffer@4.2.0: {} - smol-toml@1.7.0: {} - smol-toml@1.8.0: {} socks-proxy-agent@8.0.5: @@ -22186,7 +21859,7 @@ snapshots: socks@2.8.9: dependencies: - ip-address: 10.2.0 + ip-address: 10.7.2 smart-buffer: 4.2.0 source-map-js@1.2.1: {} @@ -22218,7 +21891,7 @@ snapshots: sprintf-js@1.1.3: optional: true - sql-escaper@1.5.1: {} + sql-escaper@1.5.2: {} ssri@13.0.1: dependencies: @@ -22315,7 +21988,9 @@ snapshots: strip-json-comments@5.0.3: {} - strnum@2.3.0: {} + strnum@2.4.2: + dependencies: + anynum: 1.0.1 structured-headers@0.4.1: {} @@ -22352,20 +22027,20 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svgo@4.0.1: + svgo@4.1.0: dependencies: commander: 11.1.0 - css-select: 5.2.2 + css-select: 6.0.0 css-tree: 3.2.1 - css-what: 6.2.2 + css-what: 7.0.0 csso: 5.0.5 picocolors: 1.1.1 - sax: 1.6.0 + sax: 1.6.1 - swagger2openapi@7.0.8: + swagger2openapi@7.0.8(encoding@0.1.13): dependencies: call-me-maybe: 1.0.2 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) node-fetch-h2: 2.3.0 node-readfiles: 0.2.0 oas-kit-common: 1.0.8 @@ -22378,7 +22053,7 @@ snapshots: transitivePeerDependencies: - encoding - tabbable@6.4.0: {} + tabbable@6.5.0: {} tagged-tag@1.0.0: {} @@ -22438,12 +22113,12 @@ snapshots: tinyclip@0.1.15: {} - tinyexec@1.2.4: {} + tinyexec@1.3.1: {} tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 tinypool@2.1.0: {} @@ -22561,7 +22236,7 @@ snapshots: ufo@1.6.4: {} - ultrahtml@1.6.0: {} + ultrahtml@1.7.0: {} unbash@4.0.11: {} @@ -22571,7 +22246,7 @@ snapshots: undici@6.28.1: {} - undici@7.27.1: {} + undici@7.29.1: {} undici@8.10.2: {} @@ -22600,47 +22275,24 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 - unifont@0.7.4: + unifont@0.7.5: dependencies: css-tree: 3.2.1 - ofetch: 1.5.1 - ohash: 2.0.11 - - unist-util-find-after@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - optional: true + ohash: 2.0.12 + undici: 8.10.2 unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 - unist-util-modify-children@4.0.0: - dependencies: - '@types/unist': 3.0.3 - array-iterate: 2.0.1 - optional: true - unist-util-position@5.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-remove-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-visit: 5.1.0 - optional: true - unist-util-stringify-position@4.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-visit-children@3.0.0: - dependencies: - '@types/unist': 3.0.3 - optional: true - unist-util-visit-parents@6.0.2: dependencies: '@types/unist': 3.0.3 @@ -22678,7 +22330,7 @@ snapshots: unplugin@3.0.0: dependencies: '@jridgewell/remapping': 2.3.5 - picomatch: 4.0.4 + picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 unrun@0.2.39: @@ -22692,7 +22344,7 @@ snapshots: chokidar: 5.0.0 destr: 2.0.5 h3: 1.15.11 - lru-cache: 11.5.1 + lru-cache: 11.5.2 node-fetch-native: 1.6.7 ofetch: 1.5.1 ufo: 1.6.4 @@ -22712,9 +22364,9 @@ snapshots: graceful-fs: 4.2.11 node-int64: 0.4.0 - update-browserslist-db@1.2.3(browserslist@4.28.2): + update-browserslist-db@1.3.3(browserslist@4.29.0): dependencies: - browserslist: 4.28.2 + browserslist: 4.29.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -22795,25 +22447,25 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0): + vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0): dependencies: '@oxc-project/types': 0.146.0 '@oxlint/plugins': 1.79.0 - '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) + '@vitest/browser': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 '@vitest/spy': 4.1.11 '@vitest/utils': 4.1.11 - '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0) - oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)) - oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.1)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)) + '@voidzero-dev/vite-plus-core': 0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0) + oxfmt: 0.64.0(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)) + oxlint: 1.79.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.3.0(@types/node@24.12.4)(bufferutil@4.1.0)(esbuild@0.28.2)(jiti@2.7.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(utf-8-validate@6.0.6)(yaml@2.9.0)) oxlint-tsgolint: 7.0.2001 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' - vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vitest: 4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) optionalDependencies: '@voidzero-dev/vite-plus-darwin-arm64': 0.3.0 '@voidzero-dev/vite-plus-darwin-x64': 0.3.0 @@ -22853,35 +22505,35 @@ snapshots: - utf-8-validate - yaml - vitefu@1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)): + vitefu@1.1.3(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)): optionalDependencies: - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@6.0.3)(unrun@0.2.39)(yaml@2.9.0)' - vitest@4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)): + vitest@4.1.11(@types/node@24.12.4)(@vitest/browser-preview@4.1.11)(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)): dependencies: '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) + '@vitest/mocker': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2)) '@vitest/pretty-format': 4.1.11 '@vitest/runner': 4.1.11 '@vitest/snapshot': 4.1.11 '@vitest/spy': 4.1.11 '@vitest/utils': 4.1.11 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.2 expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.2.1 pathe: 2.0.3 - picomatch: 4.0.4 + picomatch: 4.0.7 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.1 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' + vite: '@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0)' why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.12.4 - '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) + '@vitest/browser-preview': 4.1.11(@voidzero-dev/vite-plus-core@0.3.0(@types/node@24.12.4)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.48.0)(typescript@7.0.2)(unrun@0.2.39)(yaml@2.9.0))(bufferutil@4.1.0)(msw@2.12.11(@types/node@24.12.4)(typescript@7.0.2))(utf-8-validate@6.0.6)(vitest@4.1.11) transitivePeerDependencies: - msw @@ -23028,8 +22680,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-pm-runs@1.1.0: {} - which@2.0.2: dependencies: isexe: 2.0.0 @@ -23085,11 +22735,6 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 6.0.6 - ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): - optionalDependencies: - bufferutil: 4.1.0 - utf-8-validate: 6.0.6 - ws@8.21.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): optionalDependencies: bufferutil: 4.1.0 @@ -23102,14 +22747,16 @@ snapshots: xml-naming@0.1.0: {} + xml-naming@0.3.0: {} + xml2js@0.4.23: dependencies: - sax: 1.6.0 + sax: 1.6.1 xmlbuilder: 11.0.1 xml2js@0.6.0: dependencies: - sax: 1.6.0 + sax: 1.6.1 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -23171,7 +22818,7 @@ snapshots: dependencies: pend: 1.2.0 - yjs@13.6.31: + yjs@13.6.32: dependencies: lib0: 0.2.117 @@ -23223,14 +22870,16 @@ snapshots: grammex: 3.1.13 graphmatch: 1.1.1 - zod-to-json-schema@3.25.2(zod@4.4.3): + zod-to-json-schema@3.25.2(zod@4.6.5): dependencies: - zod: 4.4.3 + zod: 4.6.5 zod@3.25.76: {} zod@4.4.3: {} + zod@4.6.5: {} + zustand@5.0.14(@types/react@19.2.16)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6)): optionalDependencies: '@types/react': 19.2.16 From 8d051a034487ddfbfa0a7c13a25fb8a388a44d25 Mon Sep 17 00:00:00 2001 From: Wout Stiens <71498452+StiensWout@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:25:14 +0200 Subject: [PATCH 007/104] fix(web): retry failed attachment uploads after reconnect (#10338) (cherry picked from commit aff9318bf46beaf05cc7155b428d3f0b8711efd2) --- .../lib/composerAttachmentUploadQueue.test.ts | 25 ++++++ .../web/src/lib/attachmentUploadQueue.test.ts | 85 ++++++++++++++++++- apps/web/src/lib/attachmentUploadQueue.ts | 37 +++++++- 3 files changed, 145 insertions(+), 2 deletions(-) diff --git a/apps/mobile/src/lib/composerAttachmentUploadQueue.test.ts b/apps/mobile/src/lib/composerAttachmentUploadQueue.test.ts index a6f24617a4..130b007878 100644 --- a/apps/mobile/src/lib/composerAttachmentUploadQueue.test.ts +++ b/apps/mobile/src/lib/composerAttachmentUploadQueue.test.ts @@ -105,6 +105,31 @@ describe("composer attachment upload queue", () => { queue.dispose(); }); + it("retries a failed attachment when the worker restores requests after reconnect", async () => { + let states: Readonly> = {}; + const upload = vi.fn().mockRejectedValueOnce(new Error("Disconnected")).mockResolvedValue(true); + const queue = createComposerAttachmentUploadQueue({ + upload, + onChange: (next) => { + states = next; + }, + }); + const local = request("failed-upload"); + const key = composerAttachmentUploadKey(environmentId, local.attachment.id); + queue.sync([local]); + await queue.settled(); + expect(states[key]?.status).toBe("failed"); + queue.sync([local]); + await queue.settled(); + expect(upload).toHaveBeenCalledTimes(1); + queue.sync([]); + queue.sync([local]); + await queue.settled(); + expect(upload).toHaveBeenCalledTimes(2); + expect(states[key]?.status).toBe("ready"); + queue.dispose(); + }); + it("ignores a late completion after removal or environment switch", async () => { const gate = Promise.withResolvers(); const started = Promise.withResolvers(); diff --git a/apps/web/src/lib/attachmentUploadQueue.test.ts b/apps/web/src/lib/attachmentUploadQueue.test.ts index a38e107a7d..a012720899 100644 --- a/apps/web/src/lib/attachmentUploadQueue.test.ts +++ b/apps/web/src/lib/attachmentUploadQueue.test.ts @@ -1,5 +1,7 @@ import { EnvironmentId } from "@t3tools/contracts"; import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; +import { Atom, AsyncResult } from "effect/unstable/reactivity"; +import { appAtomRegistry } from "../rpc/atomRegistry"; import { composerFileNeedsReattach, @@ -10,6 +12,7 @@ import { } from "../composerDraftStore"; const mocks = vi.hoisted(() => ({ + connectionStateAtom: vi.fn(), createAssetUrl: vi.fn(), createUploadUrl: Symbol("create-upload-url"), executeAtomQuery: vi.fn(), @@ -24,7 +27,14 @@ vi.mock("@t3tools/client-runtime/state/runtime", () => ({ squashAtomCommandFailure: (result: { readonly error: unknown }) => result.error, })); -vi.mock("../rpc/atomRegistry", () => ({ appAtomRegistry: {} })); +vi.mock("../rpc/atomRegistry", async () => { + const { AtomRegistry } = await import("effect/unstable/reactivity"); + return { appAtomRegistry: AtomRegistry.make() }; +}); + +vi.mock("../connection/catalog", () => ({ + environmentCatalog: { stateAtom: mocks.connectionStateAtom }, +})); vi.mock("../state/assets", () => ({ assetEnvironment: { createUrl: mocks.createAssetUrl }, @@ -140,8 +150,22 @@ function makeFile(id: string): ComposerFileAttachment { }; } +const connectionStates = Atom.family((_environmentId: EnvironmentId) => + Atom.make(AsyncResult.success({ phase: "connected" })), +); + +function setConnected(environmentId: EnvironmentId, connected: boolean) { + appAtomRegistry.set( + connectionStates(environmentId), + AsyncResult.success({ phase: connected ? "connected" : "backoff" }), + ); +} + describe("attachmentUploadQueue", () => { beforeEach(() => { + mocks.connectionStateAtom.mockImplementation(connectionStates); + setConnected(firstEnvironment, true); + setConnected(secondEnvironment, true); TestXmlHttpRequest.requests = []; mocks.createAssetUrl.mockReset(); mocks.createAssetUrl.mockImplementation((target: unknown) => target); @@ -183,6 +207,65 @@ describe("attachmentUploadQueue", () => { vi.unstubAllGlobals(); }); + it.each([false, true])( + "retries a failed file once after reconnect, including a late HTTP failure: %s", + async (lateFailure) => { + const image = makeFile("reconnect"); + startAttachmentUpload({ environmentId: firstEnvironment, image }); + await Promise.resolve(); + const firstSettled = awaitAttachmentUploads([image.id]); + setConnected(firstEnvironment, false); + if (lateFailure) setConnected(firstEnvironment, true); + TestXmlHttpRequest.requests[0]!.complete(503); + await firstSettled; + if (!lateFailure) setConnected(firstEnvironment, true); + await Promise.resolve(); + await Promise.resolve(); + expect(TestXmlHttpRequest.requests).toHaveLength(2); + const retrySettled = awaitAttachmentUploads([image.id]); + TestXmlHttpRequest.requests[1]!.complete(503); + await retrySettled; + setConnected(firstEnvironment, true); + await Promise.resolve(); + expect(TestXmlHttpRequest.requests).toHaveLength(2); + expect(readAttachmentUpload(image.id)?.status).toBe("failed"); + + setConnected(firstEnvironment, false); + setConnected(firstEnvironment, true); + await Promise.resolve(); + await Promise.resolve(); + const finalSettled = awaitAttachmentUploads([image.id]); + TestXmlHttpRequest.requests[2]!.complete(); + await finalSettled; + expect( + getUploadedAttachments({ environmentId: firstEnvironment, images: [image] }), + ).not.toBeNull(); + setConnected(firstEnvironment, false); + setConnected(firstEnvironment, true); + await Promise.resolve(); + expect(TestXmlHttpRequest.requests).toHaveLength(3); + }, + ); + + it("does not retry for another environment or after the attachment is removed", async () => { + const image = makeFile("removed"); + startAttachmentUpload({ environmentId: firstEnvironment, image }); + await Promise.resolve(); + const settled = awaitAttachmentUploads([image.id]); + TestXmlHttpRequest.requests[0]!.complete(503); + await settled; + setConnected(secondEnvironment, false); + setConnected(secondEnvironment, true); + await Promise.resolve(); + expect(TestXmlHttpRequest.requests).toHaveLength(1); + setConnected(firstEnvironment, false); + setConnected(firstEnvironment, true); + releaseAttachmentUpload(image.id); + await Promise.resolve(); + expect(TestXmlHttpRequest.requests).toHaveLength(1); + expect(readAttachmentUpload(image.id)).toBeUndefined(); + }); + it("uploads images immediately and sends attachment references", async () => { const image = { ...makeImage("image-1"), diff --git a/apps/web/src/lib/attachmentUploadQueue.ts b/apps/web/src/lib/attachmentUploadQueue.ts index a309b95b6a..182636ab38 100644 --- a/apps/web/src/lib/attachmentUploadQueue.ts +++ b/apps/web/src/lib/attachmentUploadQueue.ts @@ -12,6 +12,8 @@ import { type PersistedAttachmentVerification, } from "@t3tools/client-runtime/state/attachments"; import { create } from "zustand"; +import * as Option from "effect/Option"; +import { AsyncResult } from "effect/unstable/reactivity"; import { DraftId, @@ -21,6 +23,7 @@ import { type ComposerThreadTarget, } from "../composerDraftStore"; import { appAtomRegistry } from "../rpc/atomRegistry"; +import { environmentCatalog } from "../connection/catalog"; import { assetEnvironment } from "../state/assets"; import { attachmentEnvironment } from "../state/attachments"; import { readPreparedConnection } from "../state/session"; @@ -58,8 +61,10 @@ interface UploadJob { attachmentId: string | null; cancelled: boolean; abort: (() => void) | null; + stopWatchingConnection: () => void; } +// Failed jobs retain their source and connection subscription until retry or release. const jobsByImageId = new Map(); const queue: UploadJob[] = []; const activeUploadsByEnvironment = new Map(); @@ -358,7 +363,11 @@ function pumpUploads(): void { } }) .finally(() => { - if (jobsByImageId.get(job.image.id) === job) { + if ( + jobsByImageId.get(job.image.id) === job && + readAttachmentUpload(job.image.id)?.status !== "failed" + ) { + job.stopWatchingConnection(); jobsByImageId.delete(job.image.id); } const remaining = (activeUploadsByEnvironment.get(job.environmentId) ?? 1) - 1; @@ -427,9 +436,34 @@ export function startAttachmentUpload(input: { attachmentId: null, cancelled: false, abort: null, + stopWatchingConnection: () => {}, }; jobsByImageId.set(input.image.id, job); + const connectionAtom = environmentCatalog.stateAtom(job.environmentId); + const isConnected = () => + Option.exists( + AsyncResult.value(appAtomRegistry.get(connectionAtom)), + (state) => state.phase === "connected", + ); + let wasConnected = isConnected(); + job.stopWatchingConnection = appAtomRegistry.subscribe(connectionAtom, () => { + const connected = isConnected(); + const reconnected = connected && !wasConnected; + wasConnected = connected; + if (!reconnected) return; + // The HTTP failure can arrive after the socket has already reconnected. + // Wait for that attempt, then retry only if this job still owns the file. + void job.settled.then(() => { + if ( + jobsByImageId.get(job.image.id) === job && + readAttachmentUpload(job.image.id)?.status === "failed" && + isConnected() + ) { + retryAttachmentUpload(input); + } + }); + }); queue.push(job); setUploadState(input.image.id, { status: "uploading", @@ -451,6 +485,7 @@ function cancelAttachmentUpload(imageId: string): void { return; } job.cancelled = true; + job.stopWatchingConnection(); jobsByImageId.delete(imageId); const queuedIndex = queue.indexOf(job); if (queuedIndex !== -1) { From 7aa6a8c5674c38ec98c18cd8d030cf903817965f Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Tue, 22 Sep 2026 12:47:45 -0600 Subject: [PATCH 008/104] fix(server): support updatedAt in ModelManifestEnvelopeSchema --- .../server/src/provider/ModelManifest.test.ts | 5 +++++ apps/server/src/provider/ModelManifest.ts | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/ModelManifest.test.ts b/apps/server/src/provider/ModelManifest.test.ts index f779280e6a..1ae51d3dc5 100644 --- a/apps/server/src/provider/ModelManifest.test.ts +++ b/apps/server/src/provider/ModelManifest.test.ts @@ -219,8 +219,12 @@ describe("resolveProviderCatalog", () => { }); }); +// Remote fixtures date after the bundle so a fetch still outranks it. +const REMOTE_UPDATED_AT = "2099-01-01T00:00:00Z"; + const REMOTE_MANIFEST: ModelManifestData = { version: 1, + updatedAt: REMOTE_UPDATED_AT, currentModels: { codex: ["remote-model"], claudeAgent: ["remote-agent-model"], @@ -229,6 +233,7 @@ const REMOTE_MANIFEST: ModelManifestData = { const REMOTE_CLAUDE_MANIFEST: ModelManifestData = { version: 1, + updatedAt: REMOTE_UPDATED_AT, currentModels: {}, providers: { claudeAgent: { diff --git a/apps/server/src/provider/ModelManifest.ts b/apps/server/src/provider/ModelManifest.ts index bcf9ccfa8b..9e92807583 100644 --- a/apps/server/src/provider/ModelManifest.ts +++ b/apps/server/src/provider/ModelManifest.ts @@ -95,6 +95,12 @@ const ManifestProviderCatalog = Schema.Struct({ */ const ModelManifestEnvelopeSchema = Schema.Struct({ version: Schema.Literal(1), + /** + * ISO date of the last edit. A release bundles its manifest, and a disk + * cache of an older edit must not outrank it. Optional so older remote + * files still decode; they count as older than any dated bundle. + */ + updatedAt: Schema.optional(Schema.String), currentModels: Schema.Record( Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(64)), Schema.Array( @@ -155,6 +161,13 @@ export const decodeManifestJson = (input: string) => export const BUNDLED_MODEL_MANIFEST: ModelManifestData = Schema.decodeUnknownSync(ModelManifestSchema)(bundledManifestJson); +/** Epoch millis of the manifest's `updatedAt`, or 0 when absent or unparsable. */ +function manifestUpdatedAtMs(manifest: ModelManifestData): number { + if (manifest.updatedAt === undefined) return 0; + const parsed = Date.parse(manifest.updatedAt); + return Number.isNaN(parsed) ? 0 : parsed; +} + export interface ResolvedManifestModel { readonly model: ServerProviderModel; readonly adapter: unknown; @@ -382,7 +395,14 @@ export const make = Effect.gen(function* () { ); if (fromDisk === null) return; // The disk copy is the last-seen remote manifest, so it outranks the - // bundle even when stale: it is refreshed on the next successful fetch. + // bundle even when stale, unless the bundle's own edit date is newer + // than the cached manifest's. Then the release carries data the cache + // has not seen and the cache is dropped so the next refresh replaces + // it. Comparing edit dates, not fetch time, keeps this independent of + // when the cache was written relative to the release. + if (manifestUpdatedAtMs(BUNDLED_MODEL_MANIFEST) > manifestUpdatedAtMs(fromDisk.manifest)) { + return; + } manifest = fromDisk.manifest; fetchedAtMs = fromDisk.fetchedAtMs; }), From 7ed3f711caa36485001538bbe1cb0017b84aa7fe Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 01:56:36 -0600 Subject: [PATCH 009/104] fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade --- .../Layers/ProjectionPipeline.test.ts | 35 ++++++ .../Layers/ProjectionPipeline.ts | 2 + .../Layers/ProjectionSnapshotQuery.test.ts | 21 ++++ .../Layers/ProjectionSnapshotQuery.ts | 7 ++ .../src/orchestration/decider.settled.test.ts | 17 +++ apps/server/src/orchestration/decider.ts | 1 + apps/server/src/persistence/Migrations.ts | 2 + .../062_RepairStoppedSessionRequests.test.ts | 82 ++++++++++++++ .../062_RepairStoppedSessionRequests.ts | 105 ++++++++++++++++++ .../src/pendingRequests.test.ts | 11 ++ .../client-runtime/src/pendingRequests.ts | 2 + 11 files changed, 285 insertions(+) create mode 100644 apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.test.ts create mode 100644 apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.ts diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts index 05b6833f77..0e73642379 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts @@ -3888,6 +3888,41 @@ it.layer(BaseTestLayer)("OrchestrationProjectionPipeline", (it) => { WHERE thread_id = 'thread-stale-user-input' `; assert.deepEqual(threadRows, [{ pendingUserInputCount: 1 }]); + + yield* appendAndProject({ + type: "thread.activity-appended", + eventId: EventId.make("evt-stale-user-input-session-stopped"), + aggregateKind: "thread", + aggregateId: ThreadId.make("thread-stale-user-input"), + occurredAt: "2026-02-26T12:35:09.000Z", + commandId: CommandId.make("cmd-stale-user-input-session-stopped"), + causationEventId: null, + correlationId: CorrelationId.make("cmd-stale-user-input-session-stopped"), + metadata: {}, + payload: { + threadId: ThreadId.make("thread-stale-user-input"), + activity: { + id: EventId.make("activity-user-input-session-stopped"), + tone: "error", + kind: "provider.user-input.respond.failed", + summary: "Provider user input response failed", + payload: { + requestId: "user-input-active", + detail: "No active provider session is bound to this thread.", + }, + turnId: null, + createdAt: "2026-02-26T12:35:09.000Z", + }, + }, + }); + assert.deepEqual( + yield* sql<{ readonly pendingUserInputCount: number }>` + SELECT pending_user_input_count AS "pendingUserInputCount" + FROM projection_threads + WHERE thread_id = 'thread-stale-user-input' + `, + [{ pendingUserInputCount: 0 }], + ); }), ); diff --git a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts index 3efc3096d6..c6aefdfe33 100644 --- a/apps/server/src/orchestration/Layers/ProjectionPipeline.ts +++ b/apps/server/src/orchestration/Layers/ProjectionPipeline.ts @@ -138,6 +138,7 @@ function isStalePendingApprovalFailureDetail(detail: string | null): boolean { return false; } return ( + detail.includes("no active provider session is bound to this thread") || detail.includes("stale pending approval request") || detail.includes("unknown pending approval request") || detail.includes("unknown pending permission request") @@ -204,6 +205,7 @@ function derivePendingUserInputCountFromActivities( activity.kind === "provider.interaction.respond.failed") && detail !== null && (detail.includes("stale pending user-input request") || + detail.includes("no active provider session is bound to this thread") || detail.includes("unknown pending user-input request") || detail.includes("unknown pending user input request") || detail.includes("unknown pending codex user input request") || diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts index 327efdc30d..bdb882dcc0 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts @@ -2670,6 +2670,27 @@ it.effect( ); projectionSnapshotLayer("ProjectionSnapshotQuery windowed thread detail", (it) => { + it.effect("does not return an orphaned question after a stopped-session reply failure", () => + Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + const snapshotQuery = yield* ProjectionSnapshotQuery; + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, turn_id, tone, kind, summary, payload_json, created_at + ) VALUES + ('orphaned-question', 'thread-orphaned', NULL, 'approval', 'user-input.requested', + 'Question', '{"requestId":"request-orphaned"}', '2026-09-24T00:00:00.000Z'), + ('orphaned-failure', 'thread-orphaned', NULL, 'error', + 'provider.user-input.respond.failed', 'Reply failed', + '{"requestId":"request-orphaned","detail":"No active provider session is bound to this thread."}', + '2026-09-24T00:00:01.000Z') + `; + const pending = yield* snapshotQuery.getPendingRequestActivities({ + threadId: ThreadId.make("thread-orphaned"), + }); + assert.deepStrictEqual(pending, []); + }), + ); // A thread shaped like real fan-out usage: user turns interleaved with // subagent turns (no user pending message), plus a turnless straggler user // message and a turnless activity anchored between turns. diff --git a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts index 046c7fd98c..097ac839b2 100644 --- a/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts +++ b/apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts @@ -1707,12 +1707,16 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () { kind IN ('approval.requested', 'approval.resolved', 'user-input.requested', 'user-input.resolved', 'interaction.requested', 'interaction.resolved') OR (kind = 'provider.approval.respond.failed' AND ( + lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%no active provider session is bound to this thread%' + OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%stale pending approval request%' OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%unknown pending approval request%' OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%unknown pending permission request%' OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%unknown pending codex approval request%' )) OR (kind = 'provider.user-input.respond.failed' AND ( + lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%no active provider session is bound to this thread%' + OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%stale pending user-input request%' OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%unknown pending user-input request%' OR lower(COALESCE(json_extract(payload_json, '$.detail'), '')) LIKE '%unknown pending user input request%' @@ -2186,6 +2190,9 @@ pending_approval_requests AS ( OR ( activity.kind = 'provider.user-input.respond.failed' AND ( + lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) LIKE '%stale pending user-input request%' OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) diff --git a/apps/server/src/orchestration/decider.settled.test.ts b/apps/server/src/orchestration/decider.settled.test.ts index dc43a4d672..d4dabdcd22 100644 --- a/apps/server/src/orchestration/decider.settled.test.ts +++ b/apps/server/src/orchestration/decider.settled.test.ts @@ -489,6 +489,23 @@ it.layer(NodeServices.layer)("settled thread decider", (it) => { const settledEvents = Array.isArray(settled) ? settled : [settled]; expect(settledEvents[0]?.type).toBe("thread.settled"); + const stoppedSession = yield* decideOrchestrationCommand({ + command: { + type: "thread.settle", + commandId: CommandId.make("cmd-settle-stopped-session-input"), + threadId: ThreadId.make("thread-1"), + }, + readModel: makeReadModel(null, null, makeSession("stopped"), [ + activity("user-input.requested", "orphaned-input", {}), + activity("provider.user-input.respond.failed", "orphaned-input", { + detail: "No active provider session is bound to this thread.", + }), + ]), + }); + expect((Array.isArray(stoppedSession) ? stoppedSession[0] : stoppedSession).type).toBe( + "thread.settled", + ); + // A non-stale respond failure (transient provider error) keeps the // request open: the user can retry, so it is still blocked-on-you. const stillOpen = yield* decideOrchestrationCommand({ diff --git a/apps/server/src/orchestration/decider.ts b/apps/server/src/orchestration/decider.ts index 40f3d1caaf..8c9a180a60 100644 --- a/apps/server/src/orchestration/decider.ts +++ b/apps/server/src/orchestration/decider.ts @@ -69,6 +69,7 @@ function isStaleRequestFailureDetail(payload: Record | null): b const detail = typeof payload?.detail === "string" ? payload.detail.toLowerCase() : null; if (detail === null) return false; return ( + detail.includes("no active provider session is bound to this thread") || detail.includes("stale pending approval request") || detail.includes("unknown pending approval request") || detail.includes("unknown pending permission request") || diff --git a/apps/server/src/persistence/Migrations.ts b/apps/server/src/persistence/Migrations.ts index ce8da3d5f3..a50bb20be3 100644 --- a/apps/server/src/persistence/Migrations.ts +++ b/apps/server/src/persistence/Migrations.ts @@ -72,6 +72,7 @@ import Migration0058 from "./Migrations/058_ProjectionProjectsAutoPull.ts"; import Migration0059 from "./Migrations/059_ProjectionThreadPullRequests.ts"; import Migration0060 from "./Migrations/060_ProjectionThreadCompactionQueue.ts"; import Migration0061 from "./Migrations/061_ProjectionThreadMessageContext.ts"; +import Migration0062 from "./Migrations/062_RepairStoppedSessionRequests.ts"; /** * Migration loader with all migrations defined inline. * @@ -176,6 +177,7 @@ const migrationEntries = [ [60, "ProjectionThreadCompactionQueue", Migration0060], // Upstream 51 is already Pylon durable rollback; retain that lineage and append. [61, "ProjectionThreadMessageContext", Migration0061], + [62, "RepairStoppedSessionRequests", Migration0062], ] as const; export const migrationManifest = migrationEntries.map(([id, name]) => [id, name] as const); diff --git a/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.test.ts b/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.test.ts new file mode 100644 index 0000000000..df7719a772 --- /dev/null +++ b/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.test.ts @@ -0,0 +1,82 @@ +import { assert, it } from "@effect/vitest"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as SqlClient from "effect/unstable/sql/SqlClient"; +import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; + +import { runMigrations } from "../Migrations.ts"; + +it.layer(Layer.mergeAll(NodeSqliteClient.layerMemory()))( + "062_RepairStoppedSessionRequests", + (it) => { + it.effect("repairs orphaned requests without dropping other pending work", () => + Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + yield* runMigrations({ toMigrationInclusive: 61 }); + + yield* sql` + INSERT INTO projection_threads ( + thread_id, project_id, title, created_at, updated_at, + pending_approval_count, pending_user_input_count + ) VALUES + ('thread-affected', 'project-1', 'Affected', '2026-09-24T00:00:00.000Z', + '2026-09-24T00:00:00.000Z', 1, 3), + ('thread-other', 'project-1', 'Other', '2026-09-24T00:00:00.000Z', + '2026-09-24T00:00:00.000Z', 0, 1) + `; + yield* sql` + INSERT INTO projection_pending_approvals ( + request_id, thread_id, status, created_at + ) VALUES ('approval-orphaned', 'thread-affected', 'pending', + '2026-09-24T00:00:01.000Z') + `; + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES + ('input-orphaned', 'thread-affected', 'approval', 'user-input.requested', + 'Question', '{"requestId":"input-orphaned"}', 1, '2026-09-24T00:00:01.000Z'), + ('input-retryable', 'thread-affected', 'approval', 'user-input.requested', + 'Question', '{"requestId":"input-retryable"}', 2, '2026-09-24T00:00:02.000Z'), + ('interaction-open', 'thread-affected', 'approval', 'interaction.requested', + 'Interaction', '{"requestId":"interaction-open"}', 3, '2026-09-24T00:00:03.000Z'), + ('input-orphaned-failed', 'thread-affected', 'error', + 'provider.user-input.respond.failed', 'Reply failed', + '{"requestId":"input-orphaned","detail":"No active provider session is bound to this thread."}', + 4, '2026-09-24T00:00:04.000Z'), + ('input-retryable-failed', 'thread-affected', 'error', + 'provider.user-input.respond.failed', 'Reply failed', + '{"requestId":"input-retryable","detail":"Provider timeout"}', + 5, '2026-09-24T00:00:05.000Z'), + ('approval-orphaned-failed', 'thread-affected', 'error', + 'provider.approval.respond.failed', 'Approval failed', + '{"requestId":"approval-orphaned","detail":"No active provider session is bound to this thread."}', + 6, '2026-09-24T00:00:06.000Z'), + ('malformed-tool', 'thread-affected', 'tool', 'tool.completed', + 'Tool completed', '{not-json', 7, '2026-09-24T00:00:07.000Z') + `; + + yield* runMigrations({ toMigrationInclusive: 62 }); + + const threads = yield* sql<{ + readonly threadId: string; + readonly pendingApprovalCount: number; + readonly pendingUserInputCount: number; + }>` + SELECT thread_id AS "threadId", pending_approval_count AS "pendingApprovalCount", + pending_user_input_count AS "pendingUserInputCount" + FROM projection_threads ORDER BY thread_id + `; + assert.deepStrictEqual(threads, [ + { threadId: "thread-affected", pendingApprovalCount: 0, pendingUserInputCount: 2 }, + { threadId: "thread-other", pendingApprovalCount: 0, pendingUserInputCount: 1 }, + ]); + + const approvals = yield* sql<{ readonly status: string }>` + SELECT status FROM projection_pending_approvals WHERE request_id = 'approval-orphaned' + `; + assert.deepStrictEqual(approvals, [{ status: "resolved" }]); + }), + ); + }, +); diff --git a/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.ts b/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.ts new file mode 100644 index 0000000000..5e724a118d --- /dev/null +++ b/apps/server/src/persistence/Migrations/062_RepairStoppedSessionRequests.ts @@ -0,0 +1,105 @@ +import * as Effect from "effect/Effect"; +import * as SqlClient from "effect/unstable/sql/SqlClient"; + +// Older projections retained native requests after the provider session had +// stopped. The reply failure is durable, so repair the cached shell counts +// without rewriting the event history. +export default Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + + yield* sql` + UPDATE projection_pending_approvals AS pending + SET status = 'resolved', decision = NULL, resolved_at = ( + SELECT MAX(activity.created_at) + FROM projection_thread_activities AS activity + WHERE activity.thread_id = pending.thread_id + AND activity.kind = 'provider.approval.respond.failed' + AND json_extract(activity.payload_json, '$.requestId') = pending.request_id + AND lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + ) + WHERE pending.status = 'pending' + AND EXISTS ( + SELECT 1 FROM projection_thread_activities AS activity + WHERE activity.thread_id = pending.thread_id + AND activity.kind = 'provider.approval.respond.failed' + AND json_extract(activity.payload_json, '$.requestId') = pending.request_id + AND activity.created_at >= pending.created_at + AND lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + ) + `; + + yield* sql` + WITH affected_threads AS ( + SELECT DISTINCT thread_id + FROM projection_thread_activities + WHERE kind = 'provider.user-input.respond.failed' + AND lower(COALESCE(json_extract(payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + ) + UPDATE projection_threads AS thread + SET pending_approval_count = ( + SELECT COUNT(*) FROM projection_pending_approvals AS approval + WHERE approval.thread_id = thread.thread_id AND approval.status = 'pending' + ), + pending_user_input_count = ( + SELECT COUNT(*) FROM ( + SELECT activity.kind, + ROW_NUMBER() OVER ( + PARTITION BY json_extract(activity.payload_json, '$.requestId') + ORDER BY activity.sequence DESC, activity.created_at DESC, + CASE WHEN activity.kind IN ('user-input.requested', 'interaction.requested') + THEN 0 ELSE 1 END DESC, + activity.activity_id DESC + ) AS request_order + FROM projection_thread_activities AS activity + WHERE activity.thread_id = thread.thread_id + AND json_type( + CASE WHEN json_valid(activity.payload_json) THEN activity.payload_json ELSE '{}' END, + '$.requestId' + ) = 'text' + AND ( + activity.kind IN ('user-input.requested', 'user-input.resolved', + 'interaction.requested', 'interaction.resolved') + OR (activity.kind = 'provider.user-input.respond.failed' AND ( + lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%stale pending user-input request%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%unknown pending user-input request%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%unknown pending user input request%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%unknown pending codex user input request%' + )) + OR (activity.kind = 'provider.interaction.respond.failed' AND ( + lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%stale pending interaction request%' + OR lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%unknown pending interaction request%' + )) + ) + ) AS latest + WHERE latest.request_order = 1 + AND latest.kind IN ('user-input.requested', 'interaction.requested') + ) + WHERE thread.thread_id IN (SELECT thread_id FROM affected_threads) + `; + + yield* sql` + UPDATE projection_threads AS thread + SET pending_approval_count = ( + SELECT COUNT(*) FROM projection_pending_approvals AS approval + WHERE approval.thread_id = thread.thread_id AND approval.status = 'pending' + ) + WHERE EXISTS ( + SELECT 1 FROM projection_thread_activities AS activity + WHERE activity.thread_id = thread.thread_id + AND activity.kind = 'provider.approval.respond.failed' + AND lower(COALESCE(json_extract(activity.payload_json, '$.detail'), '')) + LIKE '%no active provider session is bound to this thread%' + ) + `; +}); diff --git a/packages/client-runtime/src/pendingRequests.test.ts b/packages/client-runtime/src/pendingRequests.test.ts index 6fc64fdc5d..c4f345d64f 100644 --- a/packages/client-runtime/src/pendingRequests.test.ts +++ b/packages/client-runtime/src/pendingRequests.test.ts @@ -513,4 +513,15 @@ describe.each(["approval", "user-input"])("%s request completion", (requestKind) userInputs: [], }); }); + + it("closes a request when its provider session has stopped", () => { + const failed = makeActivity({ + kind: `provider.${requestKind}.respond.failed`, + payload: { + requestId: "request-1", + detail: "No active provider session is bound to this thread.", + }, + }); + expect(derivePendingRequests([requested, failed])).toEqual({ approvals: [], userInputs: [] }); + }); }); diff --git a/packages/client-runtime/src/pendingRequests.ts b/packages/client-runtime/src/pendingRequests.ts index 51488559de..b70e80c3f5 100644 --- a/packages/client-runtime/src/pendingRequests.ts +++ b/packages/client-runtime/src/pendingRequests.ts @@ -99,12 +99,14 @@ const requestActivityKinds = new Set([ // A failed reply with any other text stays open so the user can retry. const staleRequestFailureDetails = { "provider.approval.respond.failed": [ + "no active provider session is bound to this thread", "stale pending approval request", "unknown pending approval request", "unknown pending permission request", "unknown pending codex approval request", ], "provider.user-input.respond.failed": [ + "no active provider session is bound to this thread", "stale pending user-input request", "unknown pending user-input request", "unknown pending user input request", From fb5dfd014b1efaaf06dc5261118f1c16d532d35f Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 01:56:49 -0600 Subject: [PATCH 010/104] fix(server): retain fresh model bundle and device installs --- .../src/device/deviceToolMaintenance.test.ts | 31 +-------------- .../src/device/deviceToolMaintenance.ts | 38 ++++--------------- .../server/src/provider/ModelManifest.test.ts | 18 +++++++++ apps/server/src/provider/ModelManifest.ts | 3 ++ 4 files changed, 31 insertions(+), 59 deletions(-) diff --git a/apps/server/src/device/deviceToolMaintenance.test.ts b/apps/server/src/device/deviceToolMaintenance.test.ts index 4c46be11bb..e47304b46c 100644 --- a/apps/server/src/device/deviceToolMaintenance.test.ts +++ b/apps/server/src/device/deviceToolMaintenance.test.ts @@ -14,7 +14,7 @@ import { pruneLocalDeviceTools, deviceToolMaintenanceScript } from "./deviceTool const exec = NodeUtil.promisify(NodeChildProcess.execFile); describe.each([false, true])("device tool cleanup, flat=%s", (flat) => { - it("keeps current, previous, active and incomplete installs, pruning unused completed versions", async () => { + it("retains all completed installs across the install-to-spawn handoff", async () => { const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); const name = "expo-device-hub"; const directory = (version: string) => @@ -43,40 +43,13 @@ describe.each([false, true])("device tool cleanup, flat=%s", (flat) => { script, NodePath.join(directory("0.2.0"), "active-helper.cjs"), ]); - await expect(NodeFSP.stat(directory("0.1.0"))).rejects.toThrow(); - await expect(NodeFSP.stat(directory("0.3.0"))).rejects.toThrow(); - for (const version of ["0.2.0", "0.4.0", "0.5.0", "0.6.0"]) + for (const version of ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0"]) expect((await NodeFSP.stat(directory(version))).isDirectory()).toBe(true); } finally { await NodeFSP.rm(root, { recursive: true, force: true }); } }); - it("keeps every install when the process scan fails", async () => { - const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-scan-")); - try { - for (const version of ["0.1.0", "0.2.0", "0.3.0"]) { - const dir = flat - ? NodePath.join(root, `expo-device-hub@${version}`) - : NodePath.join(root, "expo-device-hub", version); - await NodeFSP.mkdir(dir, { recursive: true }); - await NodeFSP.writeFile(NodePath.join(dir, ".install-complete"), version); - } - await exec(process.execPath, [ - "-e", - deviceToolMaintenanceScript + - ` - require('node:child_process').spawnSync = () => ({ status: 1, stdout: '' }); - pruneTools(${JSON.stringify(root)}, [['expo-device-hub', '0.3.0']], ${flat}).catch(() => process.exitCode = 1); - `, - ]); - const parent = flat ? root : NodePath.join(root, "expo-device-hub"); - expect((await NodeFSP.readdir(parent)).length).toBe(3); - } finally { - await NodeFSP.rm(root, { recursive: true, force: true }); - } - }); - it("does not prune before the required version has completed installation", async () => { const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); try { diff --git a/apps/server/src/device/deviceToolMaintenance.ts b/apps/server/src/device/deviceToolMaintenance.ts index 43cc64e8fa..63a43e177b 100644 --- a/apps/server/src/device/deviceToolMaintenance.ts +++ b/apps/server/src/device/deviceToolMaintenance.ts @@ -63,36 +63,14 @@ async function withToolMaintenance(root, operation) { } } function pruneTools(root, specs, flat) { - return withToolMaintenance(root, () => { - // Keep installs used by any running helper, including older T3 releases. - const scan = process.platform === 'win32' - ? require('node:child_process').spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', 'Get-CimInstance Win32_Process | Select-Object -ExpandProperty CommandLine'], { encoding: 'utf8', timeout: 10000 }) - : require('node:child_process').spawnSync('ps', ['-ax', '-o', 'command='], { encoding: 'utf8', timeout: 10000 }); - if (scan.status !== 0 || !scan.stdout) return; - for (const [name, required] of specs) { - const parent = flat ? root : maintenancePath.join(root, name); - let names; - try { names = maintenanceFs.readdirSync(parent); } catch { continue; } - const completed = []; - for (const item of names) { - const version = flat ? (item.startsWith(name + '@') ? item.slice(name.length + 1) : '') : item; - if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[a-zA-Z0-9.-]+)?$/.test(version)) continue; - const directory = maintenancePath.join(parent, item); - try { - if (!maintenanceFs.lstatSync(directory).isDirectory()) continue; - if (maintenanceFs.readFileSync(maintenancePath.join(directory, '.install-complete'), 'utf8').trim() !== version) continue; - completed.push({ version, directory, modified: maintenanceFs.statSync(maintenancePath.join(directory, '.install-complete')).mtimeMs }); - } catch {} - } - // Never prune until the required install has completed. Retain the last other successful install. - if (!completed.some(value => value.version === required)) continue; - const previous = completed.filter(value => value.version !== required).sort((a, b) => b.modified - a.modified || b.version.localeCompare(a.version, 'en', { numeric: true }))[0]?.version; - for (const { version, directory } of completed) { - if (version === required || version === previous || scan.stdout.includes(directory + maintenancePath.sep)) continue; - maintenanceFs.rmSync(directory, { recursive: true, force: true }); - } - } - }); + // A completed tool can be selected by another host after its install lock + // is released but before the helper appears in the process list. Process + // scanning cannot prove non-use in that interval. Keep every completed + // version until installation and spawn share a cross-host lease. + void root; + void specs; + void flat; + return Promise.resolve(); } `; diff --git a/apps/server/src/provider/ModelManifest.test.ts b/apps/server/src/provider/ModelManifest.test.ts index 1ae51d3dc5..135c6980cb 100644 --- a/apps/server/src/provider/ModelManifest.test.ts +++ b/apps/server/src/provider/ModelManifest.test.ts @@ -389,6 +389,24 @@ describe("ModelManifest service", () => { ), ); + it.live("keeps a newer bundled manifest when the hosted catalog lags behind", () => + Effect.gen(function* () { + const service = yield* make; + assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); + const rebooted = yield* make; + assert.deepStrictEqual(yield* rebooted.current, BUNDLED_MODEL_MANIFEST); + }).pipe( + Effect.scoped, + Effect.provide( + serviceLayers({ + prefix: "model-manifest-older-remote-test", + response: () => + Response.json({ ...REMOTE_MANIFEST, updatedAt: "2020-01-01T00:00:00.000Z" }), + }), + ), + ), + ); + it.live("keeps the bundled manifest when the remote payload is malformed", () => Effect.gen(function* () { const service = yield* make; diff --git a/apps/server/src/provider/ModelManifest.ts b/apps/server/src/provider/ModelManifest.ts index 9e92807583..e227935d02 100644 --- a/apps/server/src/provider/ModelManifest.ts +++ b/apps/server/src/provider/ModelManifest.ts @@ -450,6 +450,9 @@ export const make = Effect.gen(function* () { Effect.catchCause(() => Effect.succeed(null)), ); if (fetched === null) return manifest; + // The hosted catalog can lag behind a newly shipped bundle. Do not + // downgrade the live model list or persist that older copy across restarts. + if (manifestUpdatedAtMs(fetched) < manifestUpdatedAtMs(manifest)) return manifest; manifest = fetched; fetchedAtMs = now; From 0776b0b05a46300eb8305003d4365a94f51a947c Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 01:58:28 -0600 Subject: [PATCH 011/104] fix(server): defer unsafe device tool pruning --- apps/server/src/device/LocalDeviceHost.ts | 11 -- .../src/device/deviceToolMaintenance.test.ts | 132 ------------------ .../src/device/deviceToolMaintenance.ts | 126 ----------------- apps/server/src/device/sshDeviceScript.ts | 3 - 4 files changed, 272 deletions(-) delete mode 100644 apps/server/src/device/deviceToolMaintenance.test.ts delete mode 100644 apps/server/src/device/deviceToolMaintenance.ts diff --git a/apps/server/src/device/LocalDeviceHost.ts b/apps/server/src/device/LocalDeviceHost.ts index 2db9c375f5..28143f5b8e 100644 --- a/apps/server/src/device/LocalDeviceHost.ts +++ b/apps/server/src/device/LocalDeviceHost.ts @@ -1,4 +1,3 @@ -import { pruneLocalDeviceTools } from "./deviceToolMaintenance.ts"; import { deviceToolInstallMessage } from "@t3tools/contracts"; /** * The device host that is this machine. @@ -583,11 +582,6 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { return yield* Effect.uninterruptibleMask((restore) => Effect.gen(function* () { const hub = yield* restore(spawnHub(hubTool)); - yield* pruneLocalDeviceTools(config.baseDir, process.execPath, "hub").pipe( - Effect.provideService(Path.Path, path), - Effect.provideService(ProcessRunner.ProcessRunner, runner), - Effect.ignore, - ); const candidate = helperPaths(hubTool); const [axExists, cliExists] = yield* restore( Effect.all([ @@ -649,11 +643,6 @@ export const make = Effect.fn("LocalDeviceHost.make")(function* () { agentToolRef = agentTool; yield* onPhase("starting"); const agentDevice = yield* startAgentDeviceDaemon(agentTool); - yield* pruneLocalDeviceTools(config.baseDir, process.execPath, "agent").pipe( - Effect.provideService(Path.Path, path), - Effect.provideService(ProcessRunner.ProcessRunner, runner), - Effect.ignore, - ); const next = { ...running, agentDevice }; yield* Ref.set(runningRef, next); return { ...toReady(next), agentDevice }; diff --git a/apps/server/src/device/deviceToolMaintenance.test.ts b/apps/server/src/device/deviceToolMaintenance.test.ts deleted file mode 100644 index e47304b46c..0000000000 --- a/apps/server/src/device/deviceToolMaintenance.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import * as Effect from "effect/Effect"; -import * as NodePathLayer from "@effect/platform-node/NodePath"; -import * as ProcessRunner from "../processRunner.ts"; -import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; -// @effect-diagnostics nodeBuiltinImport:off - tests the same standalone script used by local and SSH hosts. -import { describe, expect, it } from "@effect/vitest"; -import * as NodeFSP from "node:fs/promises"; -import * as NodeOS from "node:os"; -import * as NodePath from "node:path"; -import * as NodeChildProcess from "node:child_process"; -import * as NodeUtil from "node:util"; -import { pruneLocalDeviceTools, deviceToolMaintenanceScript } from "./deviceToolMaintenance.ts"; - -const exec = NodeUtil.promisify(NodeChildProcess.execFile); - -describe.each([false, true])("device tool cleanup, flat=%s", (flat) => { - it("retains all completed installs across the install-to-spawn handoff", async () => { - const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); - const name = "expo-device-hub"; - const directory = (version: string) => - flat ? NodePath.join(root, `${name}@${version}`) : NodePath.join(root, name, version); - try { - for (const version of ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0"]) { - await NodeFSP.mkdir(directory(version), { recursive: true }); - if (version === "0.5.0") continue; - const sentinel = NodePath.join(directory(version), ".install-complete"); - await NodeFSP.writeFile(sentinel, version); - await NodeFSP.utimes( - sentinel, - Number(version.split(".")[1]), - Number(version.split(".")[1]), - ); - } - const script = - deviceToolMaintenanceScript + - ` -(async () => { - const root = ${JSON.stringify(root)}; - await pruneTools(root, [['${name}', '0.6.0']], ${flat}); -})().catch(error => { console.error(error); process.exitCode = 1; });`; - await exec(process.execPath, [ - "-e", - script, - NodePath.join(directory("0.2.0"), "active-helper.cjs"), - ]); - for (const version of ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0", "0.6.0"]) - expect((await NodeFSP.stat(directory(version))).isDirectory()).toBe(true); - } finally { - await NodeFSP.rm(root, { recursive: true, force: true }); - } - }); - - it("does not prune before the required version has completed installation", async () => { - const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-cleanup-")); - try { - const dir = flat - ? NodePath.join(root, "expo-device-hub@0.1.0") - : NodePath.join(root, "expo-device-hub/0.1.0"); - await NodeFSP.mkdir(dir, { recursive: true }); - await NodeFSP.writeFile(NodePath.join(dir, ".install-complete"), "0.1.0"); - await exec(process.execPath, [ - "-e", - deviceToolMaintenanceScript + - `pruneTools(${JSON.stringify(root)}, [['expo-device-hub','0.6.0']], ${flat}).catch(() => process.exitCode = 1);`, - ]); - expect((await NodeFSP.stat(dir)).isDirectory()).toBe(true); - } finally { - await NodeFSP.rm(root, { recursive: true, force: true }); - } - }); -}); - -it.effect("maintenance failures retain safe context and the original process result", () => - Effect.gen(function* () { - const output = { - code: ChildProcessSpawner.ExitCode(1), - stdout: "", - stderr: "private child diagnostics", - timedOut: false, - stdoutTruncated: false, - stderrTruncated: false, - stdoutInvalidUtf8: false, - stderrInvalidUtf8: false, - }; - for (const [operation, run] of [["prune", pruneLocalDeviceTools]] as const) { - const error = yield* run("/tools", process.execPath, "hub").pipe( - Effect.provideService(ProcessRunner.ProcessRunner, { run: () => Effect.succeed(output) }), - Effect.flip, - ); - expect(error).toMatchObject({ - _tag: "DeviceToolMaintenanceError", - operation, - tool: "hub", - exitCode: 1, - cause: output, - }); - expect(error.message).toBe(`Device tool ${operation} failed for hub (exit code 1).`); - expect(error.message).not.toContain(output.stderr); - } - }).pipe(Effect.provide(NodePathLayer.layer)), -); - -it("serializes competing maintenance processes after reclaiming a stale lock", async () => { - const root = await NodeFSP.mkdtemp(NodePath.join(NodeOS.tmpdir(), "t3-tool-contention-")); - try { - const lock = NodePath.join(root, ".maintenance-lock"); - await NodeFSP.mkdir(lock); - await NodeFSP.writeFile( - NodePath.join(lock, "stale-owner.json"), - JSON.stringify({ pid: 2147483647, identity: "dead" }), - ); - const script = - deviceToolMaintenanceScript + - ` -(async () => { - const root = ${JSON.stringify(root)}; - const marker = maintenancePath.join(root, 'critical-section'); - for (let attempt = 0; attempt < 8; attempt++) await withToolMaintenance(root, () => { - maintenanceFs.writeFileSync(marker, String(process.pid), { flag: 'wx' }); - for (let check = 0; check < 100; check++) { - if (maintenanceFs.readFileSync(marker, 'utf8') !== String(process.pid)) throw Error('Overlapping maintenance'); - } - maintenanceFs.unlinkSync(marker); - }); -})().catch(error => { console.error(error); process.exitCode = 1; });`; - await Promise.all(Array.from({ length: 6 }, () => exec(process.execPath, ["-e", script]))); - await expect(NodeFSP.stat(lock)).rejects.toThrow(); - expect(await NodeFSP.readdir(root)).toEqual([]); - } finally { - await NodeFSP.rm(root, { recursive: true, force: true }); - } -}); diff --git a/apps/server/src/device/deviceToolMaintenance.ts b/apps/server/src/device/deviceToolMaintenance.ts deleted file mode 100644 index 63a43e177b..0000000000 --- a/apps/server/src/device/deviceToolMaintenance.ts +++ /dev/null @@ -1,126 +0,0 @@ -// @effect-diagnostics preferSchemaOverJson:off - JSON string literals safely embed paths and arguments in generated JavaScript. -import * as Schema from "effect/Schema"; -import * as Effect from "effect/Effect"; -import * as Path from "effect/Path"; -import * as ProcessRunner from "../processRunner.ts"; -import { AGENT_DEVICE_VERSION, DEVICE_HUB_VERSION } from "./DeviceToolchain.ts"; - -/** Shared with the SSH bootstrap. Cleanup runs only after successful startup. */ -export const deviceToolMaintenanceScript = String.raw` -const maintenanceFs = require('node:fs'); -const maintenancePath = require('node:path'); -const maintenanceAlive = pid => { - try { process.kill(pid, 0); return true; } - catch (error) { return error.code !== 'ESRCH'; } -}; -async function withToolMaintenance(root, operation) { - maintenanceFs.mkdirSync(root, { recursive: true }); - const lock = maintenancePath.join(root, '.maintenance-lock'); - const nonce = require('node:crypto').randomUUID(); - const ownerFile = process.pid + '.' + nonce + '.json'; - const candidate = lock + '.' + nonce; - const holder = { pid: process.pid }; - const deadline = Date.now() + 30000; - const removeEmptyLock = () => { - try { maintenanceFs.rmdirSync(lock); } - catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST', 'EPERM'].includes(error.code)) throw error; } - }; - maintenanceFs.mkdirSync(candidate); - try { - maintenanceFs.writeFileSync(maintenancePath.join(candidate, ownerFile), JSON.stringify(holder)); - while (true) { - try { - // Publish a populated directory atomically; rename cannot replace another populated lock. - maintenanceFs.renameSync(candidate, lock); - break; - } - catch (error) { - if (!['EEXIST', 'ENOTEMPTY', 'EPERM', 'EACCES'].includes(error.code)) throw error; - let files = []; - try { files = maintenanceFs.readdirSync(lock); } catch (error) { if (error.code !== 'ENOENT') throw error; } - if (files.length === 1) { - const previousFile = maintenancePath.join(lock, files[0]); - let previous; - try { previous = JSON.parse(maintenanceFs.readFileSync(previousFile, 'utf8')); } catch {} - if (Number.isSafeInteger(previous?.pid) && previous.pid > 0 && !maintenanceAlive(previous.pid)) { - // The unique filename belongs only to that owner. Never unlink a replacement owner's file. - try { maintenanceFs.unlinkSync(previousFile); } catch (error) { if (error.code !== 'ENOENT') throw error; } - } - } - // A concurrent acquirer publishes its owner file with the directory, so this cannot remove it. - removeEmptyLock(); - if (Date.now() >= deadline) throw Error('Device tool maintenance is locked. Retry when the other operation finishes.'); - await new Promise(resolve => setTimeout(resolve, 50)); - } - } - try { return operation(); } - finally { - maintenanceFs.unlinkSync(maintenancePath.join(lock, ownerFile)); - removeEmptyLock(); - } - } finally { - maintenanceFs.rmSync(candidate, { recursive: true, force: true }); - } -} -function pruneTools(root, specs, flat) { - // A completed tool can be selected by another host after its install lock - // is released but before the helper appears in the process list. Process - // scanning cannot prove non-use in that interval. Keep every completed - // version until installation and spawn share a cross-host lease. - void root; - void specs; - void flat; - return Promise.resolve(); -} -`; - -class DeviceToolMaintenanceError extends Schema.TaggedError()( - "DeviceToolMaintenanceError", - { - operation: Schema.Literal("prune"), - tool: Schema.Literals(["hub", "agent"]), - exitCode: Schema.NullOr(Schema.Int), - cause: Schema.Defect(), - }, -) { - override get message() { - return `Device tool ${this.operation} failed for ${this.tool} (exit code ${this.exitCode ?? "unknown"}).`; - } -} - -const runMaintenance = Effect.fn("DeviceToolchain.maintenance")(function* ( - nodePath: string, - script: string, - operation: "prune", - tool: "hub" | "agent", -) { - const runner = yield* ProcessRunner.ProcessRunner; - const result = yield* runner.run({ - command: nodePath, - args: [ - "-e", - deviceToolMaintenanceScript + - "\n" + - script + - ".catch(error => { console.error(error.message); process.exitCode = 1; });", - ], - }); - if (result.code !== 0) - return yield* Effect.fail( - new DeviceToolMaintenanceError({ operation, tool, exitCode: result.code, cause: result }), - ); -}); - -export const pruneLocalDeviceTools = Effect.fn("DeviceToolchain.prune")(function* ( - baseDir: string, - nodePath: string, - tool: "hub" | "agent", -) { - const path = yield* Path.Path; - yield* runMaintenance( - nodePath, - `pruneTools(${JSON.stringify(path.join(baseDir, "tools"))}, ${JSON.stringify(tool === "hub" ? [["expo-device-hub", DEVICE_HUB_VERSION]] : [["agent-device", AGENT_DEVICE_VERSION]])}, false)`, - "prune", - tool, - ); -}); diff --git a/apps/server/src/device/sshDeviceScript.ts b/apps/server/src/device/sshDeviceScript.ts index de412b5873..4c156870a4 100644 --- a/apps/server/src/device/sshDeviceScript.ts +++ b/apps/server/src/device/sshDeviceScript.ts @@ -1,4 +1,3 @@ -import { deviceToolMaintenanceScript } from "./deviceToolMaintenance.ts"; import { AGENT_DEVICE_VERSION, DEVICE_HUB_VERSION } from "./DeviceToolchain.ts"; export const quoteRemoteArg = (value: string) => `'${value.replaceAll("'", "'\"'\"'")}'`; @@ -29,7 +28,6 @@ const mode = ${JSON.stringify(mode)}; const hubVersion = ${JSON.stringify(DEVICE_HUB_VERSION)}; const agentVersion = ${JSON.stringify(AGENT_DEVICE_VERSION)}; ` + - deviceToolMaintenanceScript + String.raw` const fs = require('node:fs'); const path = require('node:path'); @@ -216,7 +214,6 @@ async function install(name, version, entry) { } const vendor = path.resolve(path.dirname(hubEntry), '../../vendor/serve-sim/dist'); const optional = file => fs.existsSync(file) ? file : null; - await pruneTools(path.join(root, 'tools'), [['expo-device-hub', hubVersion], ...(mode === 'agent-start' ? [['agent-device', agentVersion]] : [])], true).catch(() => {}); console.log(JSON.stringify({ nodePath: process.execPath, platforms, tools: versions(), hubPort: hub.port, ...agentResult, helpers: { serveSimAxSettings: optional(path.join(vendor, 'simax/serve-sim-ax-settings')), serveSimCli: optional(path.join(vendor, 'serve-sim.js')) } })); } finally { releaseHost(); } From 4b2070a0f779cc984fc3722a90d3cf55f69fb341 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 02:54:43 -0600 Subject: [PATCH 012/104] fix(models): date bundled catalog after Pylon edits --- apps/server/src/provider/model-manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/provider/model-manifest.json b/apps/server/src/provider/model-manifest.json index 742e30f6dd..88a01477e6 100644 --- a/apps/server/src/provider/model-manifest.json +++ b/apps/server/src/provider/model-manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "updatedAt": "2026-09-22T17:51:55Z", + "updatedAt": "2026-09-24T07:48:28Z", "currentModels": { "codex": [ "gpt-6-astra", From 762792de1c3857a60612169a5e6aa4c1614a8be9 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:17:55 -0600 Subject: [PATCH 013/104] fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. --- .../Layers/ProviderCommandReactor.test.ts | 94 +++++++++++++++++++ .../Layers/ProviderCommandReactor.ts | 15 ++- .../Layers/ProviderRuntimeIngestion.test.ts | 21 +++++ .../decider.sessionLifecycle.test.ts | 6 ++ apps/server/src/orchestration/decider.ts | 1 + 5 files changed, 136 insertions(+), 1 deletion(-) diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts index a3be323be0..8ca84fd7c1 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts @@ -2005,6 +2005,100 @@ describe("ProviderCommandReactor", () => { }), ); + effectIt.effect( + "keeps exact admission pending while a provider reports a running prior turn", + () => + Effect.gen(function* () { + const sendEntered = yield* Deferred.make(); + const releaseSend = yield* Deferred.make(); + const requestId = CommandId.make("cmd-running-provider-exact-admission"); + const messageId = asMessageId("message-running-provider-exact-admission"); + const harness = yield* Effect.promise(() => + createHarness({ + startSessionEffect: (session) => Effect.succeed({ ...session, status: "running" }), + sendTurnEffect: () => + Deferred.succeed(sendEntered, undefined).pipe( + Effect.andThen(Deferred.await(releaseSend)), + Effect.as({ threadId: ThreadId.make("thread-1"), turnId: asTurnId("new-turn") }), + ), + }), + ); + + yield* harness.engine.dispatch({ + type: "thread.turn.start", + commandId: requestId, + threadId: ThreadId.make("thread-1"), + message: { messageId, role: "user", text: "follow up", attachments: [] }, + interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, + runtimeMode: "approval-required", + createdAt: isoAt(0), + }); + yield* Deferred.await(sendEntered); + let thread = (yield* Effect.promise(() => harness.readModel())).threads.find( + (entry) => entry.id === ThreadId.make("thread-1"), + ); + expect(thread?.session?.status).toBe("starting"); + expect(thread?.session?.pendingTurnRequestId).toBe(requestId); + expect(thread?.session?.pendingTurnMessageId).toBe(messageId); + + yield* Deferred.succeed(releaseSend, undefined); + yield* Effect.promise(() => + waitFor(async () => { + const thread = (await harness.readModel()).threads.find( + (entry) => entry.id === ThreadId.make("thread-1"), + ); + return thread?.session?.activeTurnId === asTurnId("new-turn"); + }), + ); + thread = (yield* Effect.promise(() => harness.readModel())).threads.find( + (entry) => entry.id === ThreadId.make("thread-1"), + ); + expect(thread?.session?.status).toBe("running"); + expect(thread?.session?.activeTurnRequestId).toBe(requestId); + expect(thread?.session?.pendingTurnRequestId).toBeUndefined(); + }), + ); + + for (const status of ["error", "closed"] as const) { + effectIt.effect(`rejects exact admission when the provider session is ${status}`, () => + Effect.gen(function* () { + const requestId = CommandId.make(`cmd-terminal-provider-${status}`); + const harness = yield* Effect.promise(() => + createHarness({ + startSessionEffect: (session) => Effect.succeed({ ...session, status }), + }), + ); + yield* harness.engine.dispatch({ + type: "thread.turn.start", + commandId: requestId, + threadId: ThreadId.make("thread-1"), + message: { + messageId: asMessageId(`message-terminal-provider-${status}`), + role: "user", + text: "follow up", + attachments: [], + }, + interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE, + runtimeMode: "approval-required", + createdAt: isoAt(0), + }); + yield* Effect.promise(() => + waitFor(async () => { + const thread = (await harness.readModel()).threads.find( + (entry) => entry.id === ThreadId.make("thread-1"), + ); + return thread?.session?.status === "error"; + }), + ); + const thread = (yield* Effect.promise(() => harness.readModel())).threads.find( + (entry) => entry.id === ThreadId.make("thread-1"), + ); + expect(thread?.session?.failedTurnRequestId).toBe(requestId); + expect(harness.sendTurn).not.toHaveBeenCalled(); + }), + ); + } + effectIt.effect("does not disarm when raw turn start ingestion never accepts the CAS", () => Effect.gen(function* () { const testClock = yield* TestClock.make(); diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index e5fcf3d49c..fd9d22bb73 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -1048,10 +1048,23 @@ const make = Effect.gen(function* () { detail: `Provider session '${session.threadId}' started without a provider instance or session incarnation id.`, }); } + if ( + options?.pendingTurnStart === true && + (session.status === "error" || session.status === "closed") + ) { + return yield* new ProviderAdapterRequestError({ + provider: providerErrorLabel(session.provider), + method: "thread.turn.start", + detail: `Provider session '${session.threadId}' is ${session.status}; it cannot accept a new turn.`, + }); + } const sessionBinding: OrchestrationSession = { threadId, + // The provider can still report the previous turn as running while + // a new exact admission is reserved. The reservation owns the + // projected lifecycle until its matching turn.started is accepted. status: - options?.pendingTurnStart === true && session.status === "ready" + options?.pendingTurnStart === true ? "starting" : mapProviderSessionStatusToOrchestrationStatus(session.status), providerName: session.provider, diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts index 9bb1d25e74..91cdbb9373 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts @@ -1663,6 +1663,27 @@ describe("ProviderRuntimeIngestion", () => { expect(thread?.session?.pendingTurnRequestId).toBe(requestId); expect(thread?.messages.some((message) => message.role === "assistant")).toBe(false); + // The prior turn can finish on the same provider session after the new + // admission is reserved. Its terminal event must not clear that pending + // admission before the matching turn.started arrives. + await harness.emitAndDrain([ + { + type: "turn.completed", + eventId: asEventId("evt-runtime-session-b-prior-turn-completed"), + provider: ProviderDriverKind.make("codex"), + providerInstanceId: ProviderInstanceId.make("codex"), + threadId, + turnId: asTurnId("turn-runtime-session-b-prior"), + admissionRequestId: CommandId.make("cmd-runtime-session-b-prior"), + sessionIncarnationId: sessionB, + createdAt: "2026-01-01T00:00:02.500Z", + payload: { state: "completed" }, + }, + ]); + thread = (await harness.readModel()).threads.find((entry) => entry.id === threadId); + expect(thread?.session?.status).toBe("starting"); + expect(thread?.session?.pendingTurnRequestId).toBe(requestId); + const currentTurnId = asTurnId("turn-runtime-session-b"); harness.emit({ type: "turn.started", diff --git a/apps/server/src/orchestration/decider.sessionLifecycle.test.ts b/apps/server/src/orchestration/decider.sessionLifecycle.test.ts index f60a02abe8..79b2158d40 100644 --- a/apps/server/src/orchestration/decider.sessionLifecycle.test.ts +++ b/apps/server/src/orchestration/decider.sessionLifecycle.test.ts @@ -199,6 +199,12 @@ it.layer(NodeServices.layer)("session lifecycle CAS decider", (it) => { readModel: makeReadModel(current), }); expect(stale).toEqual([]); + + const runningWithPendingRequest = yield* decideOrchestrationCommand({ + command: { ...command, session: { ...acceptedSession, status: "running" } }, + readModel: makeReadModel(current), + }); + expect(runningWithPendingRequest).toEqual([]); }), ); diff --git a/apps/server/src/orchestration/decider.ts b/apps/server/src/orchestration/decider.ts index 8c9a180a60..5afe8eb9a2 100644 --- a/apps/server/src/orchestration/decider.ts +++ b/apps/server/src/orchestration/decider.ts @@ -2629,6 +2629,7 @@ export const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand" thread.session.activeTurnId !== null || command.session.pendingTurnRequestId !== command.requestId || command.session.pendingTurnMessageId !== command.messageId || + command.session.status !== "starting" || command.session.providerInstanceId !== command.modelSelection.instanceId || command.session.runtimeMode !== command.runtimeMode || command.session.sessionIncarnationId === undefined || From a8d90411ddb7d9432bb605b02798e65ee013bbf1 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:19:23 -0600 Subject: [PATCH 014/104] feat(chat): deliver buffered prose at natural boundaries (#757) --- .../Layers/ProviderRuntimeIngestion.test.ts | 250 ++++++++++++++++++ .../Layers/ProviderRuntimeIngestion.ts | 92 ++++++- apps/web/src/components/ChatMarkdown.tsx | 11 +- apps/web/src/index.css | 13 + 4 files changed, 362 insertions(+), 4 deletions(-) diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts index 91cdbb9373..dd5dd4f3dc 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts @@ -68,6 +68,7 @@ import * as ThreadPlanProgress from "../ThreadPlanProgress.ts"; import { ProviderRuntimeIngestionLive, runtimeEventToActivities, + splitBufferedAssistantText, } from "./ProviderRuntimeIngestion.ts"; import { DEFAULT_THREAD_TITLE } from "../threadTitles.ts"; import { OrchestrationEngineService } from "../Services/OrchestrationEngine.ts"; @@ -488,7 +489,23 @@ describe("ProviderRuntimeIngestion", () => { }); }), ).pipe(Layer.provide(projectionSnapshotLayer)); + // Advance pacing deterministically while keeping real sleeps and deadlines. + let clockOffsetMs = 0; + const realClock = Effect.runSync(Effect.service(Clock.Clock)); + const shiftedClock: Clock.Clock = { + currentTimeMillisUnsafe: () => realClock.currentTimeMillisUnsafe() + clockOffsetMs, + currentTimeMillis: Effect.sync(() => realClock.currentTimeMillisUnsafe() + clockOffsetMs), + currentTimeNanosUnsafe: () => + realClock.currentTimeNanosUnsafe() + BigInt(clockOffsetMs) * 1_000_000n, + currentTimeNanos: Effect.sync( + () => realClock.currentTimeNanosUnsafe() + BigInt(clockOffsetMs) * 1_000_000n, + ), + monotonicTimeNanosUnsafe: () => realClock.monotonicTimeNanosUnsafe(), + monotonicTimeNanos: realClock.monotonicTimeNanos, + sleep: (duration) => realClock.sleep(duration), + }; const layer = ProviderRuntimeIngestionLive.pipe( + Layer.provide(Layer.succeed(Clock.Clock, shiftedClock)), Layer.provideMerge(orchestrationLayer), Layer.provideMerge(ingestionProjectionSnapshotLayer), // Single shared liveness instance across ingestion (writer), the @@ -587,6 +604,9 @@ describe("ProviderRuntimeIngestion", () => { dispatch, readModel: () => testRuntime.runPromise(snapshotQuery.getSnapshot()), emit: provider.emit, + advanceClock: (ms: number) => { + clockOffsetMs += ms; + }, emitAndDrain, sqlCount: sqlCounter.count, readTurn: (turnId: TurnId) => @@ -4986,6 +5006,145 @@ describe("ProviderRuntimeIngestion", () => { expect(finalMessage?.streaming).toBe(false); }); + it("delivers finished paragraphs while the rest of the message stays buffered", async () => { + const harness = await createHarness(); + const now = "2026-01-01T00:00:00.000Z"; + const codex = ProviderDriverKind.make("codex"); + const threadId = asThreadId("thread-1"); + const turnId = asTurnId("turn-paragraph-flush"); + const itemId = asItemId("item-paragraph-flush"); + + harness.emit({ + type: "turn.started", + eventId: asEventId("evt-paragraph-started"), + provider: codex, + createdAt: now, + threadId, + turnId, + }); + await waitForThread( + harness.readModel, + (thread) => thread.session?.status === "running" && thread.session?.activeTurnId === turnId, + ); + + // Each delta lands well outside the pacing window of the one before. + const emitDelta = (eventId: string, delta: string) => { + harness.advanceClock(1_000); + harness.emit({ + type: "content.delta", + eventId: asEventId(eventId), + provider: codex, + createdAt: now, + threadId, + turnId, + itemId, + payload: { streamKind: "assistant_text", delta }, + }); + }; + + emitDelta("evt-paragraph-1", "First paragraph.\n\nSecond para"); + const afterFirst = await waitForThread(harness.readModel, (thread) => + thread.messages.some( + (message: ProviderRuntimeTestMessage) => message.id === `assistant:${itemId}`, + ), + ); + expect( + afterFirst.messages.find((m: ProviderRuntimeTestMessage) => m.id === `assistant:${itemId}`), + ).toMatchObject({ + text: "First paragraph.\n\n", + streaming: true, + }); + + // An open code block holds the whole block until its closing fence lands. + emitDelta("evt-paragraph-2", "graph.\n\n```ts\nconst a = 1;\n\nconst b = 2;\n"); + await harness.drain(); + expect( + (await harness.readModel()).threads + .find((t) => t.id === threadId) + ?.messages.find((m: ProviderRuntimeTestMessage) => m.id === `assistant:${itemId}`)?.text, + ).toBe("First paragraph.\n\nSecond paragraph.\n\n"); + + emitDelta("evt-paragraph-3", "```\n\nTail without newline"); + harness.emit({ + type: "item.completed", + eventId: asEventId("evt-paragraph-completed"), + provider: codex, + createdAt: now, + threadId, + turnId, + itemId, + payload: { itemType: "assistant_message", status: "completed" }, + }); + const finalThread = await waitForThread(harness.readModel, (thread) => + thread.messages.some( + (message: ProviderRuntimeTestMessage) => + message.id === `assistant:${itemId}` && !message.streaming, + ), + ); + expect( + finalThread.messages.find((m: ProviderRuntimeTestMessage) => m.id === `assistant:${itemId}`) + ?.text, + ).toBe( + "First paragraph.\n\nSecond paragraph.\n\n```ts\nconst a = 1;\n\nconst b = 2;\n```\n\nTail without newline", + ); + }); + + it("holds paragraphs that finish inside the pacing window and lands them together", async () => { + const harness = await createHarness(); + const codex = ProviderDriverKind.make("codex"); + const threadId = asThreadId("thread-1"); + const turnId = asTurnId("turn-paced"); + const itemId = asItemId("item-paced"); + // Every delta carries the same event time, like OpenCode does for one + // part. Pacing must follow the server clock, not the event stamp. + const now = "2026-01-01T00:00:00.000Z"; + + harness.emit({ + type: "turn.started", + eventId: asEventId("evt-paced-started"), + provider: codex, + createdAt: now, + threadId, + turnId, + }); + await waitForThread( + harness.readModel, + (thread) => thread.session?.status === "running" && thread.session?.activeTurnId === turnId, + ); + // Emit is fire-and-forget, so drain after each delta before moving the + // clock. Otherwise the worker reads a clock that has already advanced. + let clockMs = 0; + const emitDelta = async (eventId: string, delta: string, offsetMs: number) => { + harness.advanceClock(offsetMs - clockMs); + clockMs = offsetMs; + await harness.emitAndDrain([ + { + type: "content.delta", + eventId: asEventId(eventId), + provider: codex, + createdAt: now, + threadId, + turnId, + itemId, + payload: { streamKind: "assistant_text", delta }, + }, + ]); + }; + const messageText = async () => + (await harness.readModel()).threads + .find((t) => t.id === threadId) + ?.messages.find((m: ProviderRuntimeTestMessage) => m.id === `assistant:${itemId}`)?.text; + + await emitDelta("evt-paced-1", "One.\n\n", 0); + await emitDelta("evt-paced-2", "Two.\n\n", 100); + await emitDelta("evt-paced-3", "Three.\n\n", 200); + // The first paragraph lands right away. The next two are inside the window. + expect(await messageText()).toBe("One.\n\n"); + + await emitDelta("evt-paced-4", "Four.\n\n", 500); + expect(await messageText()).toBe("One.\n\nTwo.\n\nThree.\n\nFour.\n\n"); + }); + it("spills oversized buffered deltas and still finalizes full assistant text", async () => { const harness = await createHarness(); const now = "2026-01-01T00:00:00.000Z"; @@ -6818,3 +6977,94 @@ describe("ProviderRuntimeIngestion", () => { expect(thread.session?.lastError).toBe("runtime still processed"); }); }); + +describe("splitBufferedAssistantText", () => { + it("keeps a partial trailing line buffered", () => { + expect(splitBufferedAssistantText("one\n\ntwo")).toEqual({ ready: "one\n\n", rest: "two" }); + expect(splitBufferedAssistantText("one\ntwo")).toEqual({ ready: "", rest: "one\ntwo" }); + }); + + it("does not split inside an open fence and delivers the block at its closing fence", () => { + const open = "intro\n\n```\ncode\n\nmore\n"; + expect(splitBufferedAssistantText(open)).toEqual({ + ready: "intro\n\n", + rest: "```\ncode\n\nmore\n", + }); + expect(splitBufferedAssistantText(`${open}\`\`\`\nafter`)).toEqual({ + ready: `${open}\`\`\`\n`, + rest: "after", + }); + }); + + it("does not treat a fence with an info string as a closing fence", () => { + const text = "```\n```javascript\nstill code\n\nmore\n"; + expect(splitBufferedAssistantText(text)).toEqual({ ready: "", rest: text }); + }); + + it("treats a fence indented four or more spaces as code, not a closing fence", () => { + const text = "```\n ```\n\nstill code\n"; + expect(splitBufferedAssistantText(text)).toEqual({ ready: "", rest: text }); + expect(splitBufferedAssistantText("```\n ```\nafter")).toEqual({ + ready: "```\n ```\n", + rest: "after", + }); + }); + + it("keeps a fence nested under a list item open across its blank lines", () => { + const text = "- step\n\n ```ts\n a\n\n b\n ```\n\nafter\n"; + expect(splitBufferedAssistantText(text)).toEqual({ + ready: "- step\n\n ```ts\n a\n\n b\n ```\n\n", + rest: "after\n", + }); + }); + + it("does not treat a no-break-space line as blank", () => { + expect(splitBufferedAssistantText("para\n\u00a0\ncont\n\nnext")).toEqual({ + ready: "para\n\u00a0\ncont\n\n", + rest: "next", + }); + }); + + it("treats CRLF blank lines as boundaries", () => { + expect(splitBufferedAssistantText("one\r\n\r\ntwo")).toEqual({ + ready: "one\r\n\r\n", + rest: "two", + }); + }); + + it("only closes a fence with the same marker of equal or greater length", () => { + const text = "````\n```\nstill code\n\n````\n\nout\n"; + expect(splitBufferedAssistantText(text)).toEqual({ + ready: "````\n```\nstill code\n\n````\n\n", + rest: "out\n", + }); + expect(splitBufferedAssistantText("~~~\n```\n\nx\n")).toEqual({ + ready: "", + rest: "~~~\n```\n\nx\n", + }); + }); + + it("delivers tight list items one at a time", () => { + expect(splitBufferedAssistantText("## Steps\n\n- one\n- two\n- thr")).toEqual({ + ready: "## Steps\n\n- one\n- two\n", + rest: "- thr", + }); + expect(splitBufferedAssistantText("1. one\n2. two\n more\n3. t")).toEqual({ + ready: "1. one\n2. two\n more\n", + rest: "3. t", + }); + }); + + it("keeps a partial list marker and list-like code buffered", () => { + expect(splitBufferedAssistantText("intro\n-")).toEqual({ ready: "", rest: "intro\n-" }); + expect(splitBufferedAssistantText("intro\n1.")).toEqual({ ready: "", rest: "intro\n1." }); + // `intro\n- \n` would parse as a setext heading, so a bare marker with only + // trailing whitespace is not a boundary on the partial line either. + expect(splitBufferedAssistantText("intro\n- ")).toEqual({ ready: "", rest: "intro\n- " }); + expect(splitBufferedAssistantText("- one\n")).toEqual({ ready: "", rest: "- one\n" }); + expect(splitBufferedAssistantText("```\n- one\n- two\n")).toEqual({ + ready: "", + rest: "```\n- one\n- two\n", + }); + }); +}); diff --git a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts index 7aea6f4d5d..4012a4089f 100644 --- a/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts +++ b/apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.ts @@ -27,6 +27,7 @@ import { } from "@t3tools/contracts"; import * as Cache from "effect/Cache"; import * as Cause from "effect/Cause"; +import * as Clock from "effect/Clock"; import * as Crypto from "effect/Crypto"; import * as Context from "effect/Context"; import * as Data from "effect/Data"; @@ -158,6 +159,7 @@ const BUFFERED_PROPOSED_PLAN_BY_ID_TTL = Duration.minutes(120); const TASK_DESCRIPTION_BY_TASK_CACHE_CAPACITY = 10_000; const TASK_DESCRIPTION_BY_TASK_TTL = Duration.minutes(120); const MAX_BUFFERED_ASSISTANT_CHARS = 24_000; +const MIN_ASSISTANT_DELIVERY_INTERVAL_MS = 400; const STRICT_PROVIDER_LIFECYCLE_GUARD = process.env.T3CODE_STRICT_PROVIDER_LIFECYCLE_GUARD !== "0"; type TurnStartRequestedDomainEvent = Extract< @@ -268,6 +270,51 @@ function hasRenderableAssistantText(text: string | undefined): boolean { return (text?.trim().length ?? 0) > 0; } +// A closing fence may be indented at most three spaces more than its opener. +const MARKDOWN_FENCE_PATTERN = /^( *)(`{3,}|~{3,})/; +const BLANK_LINE_PATTERN = /^[ \t]*$/; +// A complete list marker lets the previous item land before the next is done. +const LIST_ITEM_START_PATTERN = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]/; + +/** Return text whose Markdown shape is complete, leaving the active block buffered. */ +export function splitBufferedAssistantText(text: string): { ready: string; rest: string } { + let openFence: { marker: string; indent: number } | null = null; + let boundary = -1; + let lineStart = 0; + for (;;) { + const newline = text.indexOf("\n", lineStart); + const line = text + .slice(lineStart, newline === -1 ? text.length : newline) + .replace(/[ \t\r]+$/, ""); + if (openFence === null && lineStart > 0 && LIST_ITEM_START_PATTERN.test(line)) { + boundary = lineStart; + } + if (newline === -1) break; + const fenceMatch = MARKDOWN_FENCE_PATTERN.exec(line); + if (fenceMatch) { + const indent = fenceMatch[1]!.length; + const marker = fenceMatch[2]!; + if (openFence === null) { + openFence = { marker, indent }; + } else if ( + marker[0] === openFence.marker[0] && + marker.length >= openFence.marker.length && + indent <= openFence.indent + 3 && + line.length === indent + marker.length + ) { + openFence = null; + boundary = newline + 1; + } + } else if (openFence === null && BLANK_LINE_PATTERN.test(line) && lineStart > 0) { + boundary = newline + 1; + } + lineStart = newline + 1; + } + return boundary === -1 + ? { ready: "", rest: text } + : { ready: text.slice(0, boundary), rest: text.slice(boundary) }; +} + function proposedPlanIdForTurn(threadId: ThreadId, turnId: TurnId): string { return `plan:${threadId}:turn:${turnId}`; } @@ -1618,6 +1665,11 @@ const make = Effect.gen(function* () { timeToLive: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL, lookup: () => Effect.succeed(""), }); + const lastAssistantDeliveryAtByMessageId = yield* Cache.make({ + capacity: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_CACHE_CAPACITY, + timeToLive: BUFFERED_MESSAGE_TEXT_BY_MESSAGE_ID_TTL, + lookup: () => Effect.succeed(0), + }); const assistantSegmentStateByTurnKey = yield* Cache.make({ capacity: TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY, @@ -1809,7 +1861,7 @@ const make = Effect.gen(function* () { }); }); - const appendBufferedAssistantText = (messageId: MessageId, delta: string) => + const appendBufferedAssistantText = (messageId: MessageId, delta: string, atMillis: number) => Cache.getOption(bufferedAssistantTextByMessageId, messageId).pipe( Effect.flatMap((existingText) => Effect.gen(function* () { @@ -1817,6 +1869,30 @@ const make = Effect.gen(function* () { onNone: () => delta, onSome: (text) => `${text}${delta}`, }); + const { ready, rest } = splitBufferedAssistantText(nextText); + const lastDeliveredAt = Option.getOrUndefined( + yield* Cache.getOption(lastAssistantDeliveryAtByMessageId, messageId), + ); + if ( + (lastDeliveredAt === undefined || + atMillis - lastDeliveredAt >= MIN_ASSISTANT_DELIVERY_INTERVAL_MS) && + hasRenderableAssistantText(ready) && + rest.length <= MAX_BUFFERED_ASSISTANT_CHARS + ) { + if (rest.length > 0) { + yield* commitRuntimeMutation( + Cache.set(bufferedAssistantTextByMessageId, messageId, rest), + ); + } else { + yield* commitRuntimeMutation( + Cache.invalidate(bufferedAssistantTextByMessageId, messageId), + ); + } + yield* commitRuntimeMutation( + Cache.set(lastAssistantDeliveryAtByMessageId, messageId, atMillis), + ); + return ready; + } if (nextText.length <= MAX_BUFFERED_ASSISTANT_CHARS) { yield* commitRuntimeMutation( Cache.set(bufferedAssistantTextByMessageId, messageId, nextText), @@ -1843,7 +1919,11 @@ const make = Effect.gen(function* () { ); const clearBufferedAssistantText = (messageId: MessageId) => - commitRuntimeMutation(Cache.invalidate(bufferedAssistantTextByMessageId, messageId)); + commitRuntimeMutation(Cache.invalidate(bufferedAssistantTextByMessageId, messageId)).pipe( + Effect.andThen( + commitRuntimeMutation(Cache.invalidate(lastAssistantDeliveryAtByMessageId, messageId)), + ), + ); const appendBufferedProposedPlan = (planId: string, delta: string, createdAt: string) => Cache.getOption(bufferedProposedPlanById, planId).pipe( @@ -2752,7 +2832,13 @@ const make = Effect.gen(function* () { : "buffered", ); if (assistantDeliveryMode === "buffered") { - const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, assistantDelta); + // Provider event timestamps may be fixed for an entire part; pace + // delivery by the ingestion clock instead. + const spillChunk = yield* appendBufferedAssistantText( + assistantMessageId, + assistantDelta, + yield* Clock.currentTimeMillis, + ); if (spillChunk.length > 0) { yield* orchestrationEngine.dispatch({ type: "thread.message.assistant.delta", diff --git a/apps/web/src/components/ChatMarkdown.tsx b/apps/web/src/components/ChatMarkdown.tsx index 9ef4f6138d..26e4410c2f 100644 --- a/apps/web/src/components/ChatMarkdown.tsx +++ b/apps/web/src/components/ChatMarkdown.tsx @@ -3307,7 +3307,15 @@ const CHAT_MARKDOWN_COMPONENTS = { resetKeys={[codeBlock.code, language, diffThemeName, isStreaming]} fallback={
{children}
} > - {children}}> + {/* Keep the block's dimensions while highlighting loads so a + newly delivered code block does not flash as plain text. */} + + {children} + + } + > diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 90c63f62ba..d7c49958ff 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -1911,6 +1911,19 @@ code { background: transparent !important; } +/* A completed block can arrive before the assistant finishes. Animate only + fresh live blocks, and honor Pylon's panel-motion and reduced-motion controls. */ +@media (prefers-reduced-motion: no-preference) { + [data-panel-animations="true"] .chat-markdown[data-streaming] > *, + [data-panel-animations="true"] .chat-markdown[data-streaming] .chat-markdown-shiki { + transition: opacity var(--panel-animation-duration, 600ms) ease-out; + + @starting-style { + opacity: 0; + } + } +} + /* Diagnostics-style tables: row separators only, uppercase headers, and a scroll-fade container for horizontal overflow. The root chat-markdown wrapping rules (overflow-wrap: anywhere) would let columns shrink to single From 9cadbaae264e45e4bbb049b3c62d69864bed53bb Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:19:26 -0600 Subject: [PATCH 015/104] fix(server): show Claude subagent tool progress (#745) --- .../src/provider/Layers/ClaudeAdapter.test.ts | 66 +++++++++++++++++++ .../src/provider/Layers/ClaudeAdapter.ts | 9 ++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts index 028322f50c..4565e8dccd 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts @@ -10179,4 +10179,70 @@ describe("ClaudeAdapterLive", () => { Effect.provide(harness.layer), ); }); + + it.effect("attributes live subagent tool heartbeats when Claude omits task_id", () => { + const harness = makeHarness(); + return Effect.gen(function* () { + const adapter = yield* ClaudeAdapter; + const progressFiber = yield* adapter.streamEvents.pipe( + Stream.filter((event) => event.type === "tool.progress"), + Stream.take(4), + Stream.runCollect, + Effect.forkChild, + ); + const session = yield* adapter.startSession({ + threadId: THREAD_ID, + provider: ProviderDriverKind.make("claudeAgent"), + runtimeMode: "full-access", + }); + yield* adapter.sendTurn({ threadId: session.threadId, input: "Delegate work" }); + + harness.query.emit({ + type: "system", + subtype: "task_started", + task_id: "task-heartbeat", + description: "Inspect code", + task_type: "local_agent", + tool_use_id: "tool-task-heartbeat", + uuid: "task-heartbeat-start", + session_id: "sdk-heartbeat", + } as unknown as SDKMessage); + const heartbeat = (uuid: string, parentToolUseId: string, taskId?: string) => + harness.query.emit({ + type: "tool_progress", + tool_use_id: `tool-${uuid}`, + tool_name: "Bash", + elapsed_time_seconds: 30, + parent_tool_use_id: parentToolUseId, + ...(taskId ? { task_id: taskId } : {}), + uuid, + session_id: "sdk-heartbeat", + } as unknown as SDKMessage); + heartbeat("live", "tool-task-heartbeat"); + heartbeat("explicit", "tool-task-heartbeat", "task-explicit"); + heartbeat("unknown", "tool-unknown"); + harness.query.emit({ + type: "system", + subtype: "task_notification", + task_id: "task-heartbeat", + status: "completed", + uuid: "task-heartbeat-end", + session_id: "sdk-heartbeat", + } as unknown as SDKMessage); + heartbeat("late", "tool-task-heartbeat"); + + const progress = Array.from(yield* Fiber.join(progressFiber)); + assert.deepEqual( + progress.map((event) => + event.type === "tool.progress" && event.payload.taskId + ? String(event.payload.taskId) + : undefined, + ), + ["task-heartbeat", "task-explicit", undefined, undefined], + ); + }).pipe( + Effect.provideService(Random.Random, makeDeterministicRandomService()), + Effect.provide(harness.layer), + ); + }); }); diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts index 3f74b6b855..e45474b80a 100644 --- a/apps/server/src/provider/Layers/ClaudeAdapter.ts +++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts @@ -4346,6 +4346,13 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( }; if (message.type === "tool_progress") { + // Claude omits task_id on subagent tool heartbeats but includes the + // launching Task tool's id. Recover only a still-live agent: a late + // heartbeat for a completed task must not revive its activity row. + const owningTaskId = agentIdForParentToolUse(context.taskAgents, message.parent_tool_use_id); + const taskId = + message.task_id ?? + (owningTaskId && context.liveTaskIds.has(owningTaskId) ? owningTaskId : undefined); yield* offerRuntimeEvent(context.sessionIncarnationId, { ...base, type: "tool.progress", @@ -4353,7 +4360,7 @@ export const makeClaudeAdapter = Effect.fn("makeClaudeAdapter")(function* ( toolUseId: message.tool_use_id, toolName: message.tool_name, elapsedSeconds: message.elapsed_time_seconds, - ...(message.task_id ? { taskId: RuntimeTaskId.make(message.task_id) } : {}), + ...(taskId ? { taskId: RuntimeTaskId.make(taskId) } : {}), ...(message.parent_tool_use_id !== null ? { parentToolUseId: message.parent_tool_use_id } : {}), From a675fa8db072199d25339fb1fe7069c40e26e073 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:19:29 -0600 Subject: [PATCH 016/104] fix(server): show Relay workers from installed CLI results (#744) --- .../orchestration/RelayWorkerBridge.test.ts | 259 ++++++++++++++++- .../src/orchestration/RelayWorkerBridge.ts | 265 ++++++++++++++++-- 2 files changed, 491 insertions(+), 33 deletions(-) diff --git a/apps/server/src/orchestration/RelayWorkerBridge.test.ts b/apps/server/src/orchestration/RelayWorkerBridge.test.ts index 727e5260ce..ab4a135e61 100644 --- a/apps/server/src/orchestration/RelayWorkerBridge.test.ts +++ b/apps/server/src/orchestration/RelayWorkerBridge.test.ts @@ -213,6 +213,65 @@ describe("RelayWorkerBridge direct receipts", () => { ), ).toEqual([]); }); + + it("binds the installed Claude plugin's unversioned job result", () => { + const result = { + type: "tool_result", + content: [ + { + type: "text", + text: JSON.stringify({ + id: jobId, + status: "queued", + providerType: "codex", + createdAt: "2026-09-24T00:00:00.000Z", + }), + }, + ], + }; + expect( + relayBindingFromToolEvent( + toolEvent({ + toolName: "mcp__plugin_relay-orchestrator_relay__relay_delegate", + result, + }), + ), + ).toEqual([ + { kind: "job", id: jobId, threadId: "thread-a", turnId: "turn-a", toolCallId: "tool-a" }, + ]); + expect( + relayBindingFromToolEvent( + toolEvent({ + toolName: "mcp__relay__relay_delegate", + result, + }), + ), + ).toEqual([ + { kind: "job", id: jobId, threadId: "thread-a", turnId: "turn-a", toolCallId: "tool-a" }, + ]); + expect( + relayBindingFromToolEvent( + toolEvent({ + toolName: "mcp__plugin_relay-orchestrator_relay__relay_panel", + result: { + type: "tool_result", + content: [ + { + type: "text", + text: JSON.stringify({ + id: panelId, + jobs: [jobId], + unstarted: [{ index: 1 }], + }), + }, + ], + }, + }), + ), + ).toEqual([ + { kind: "panel", id: panelId, threadId: "thread-a", turnId: "turn-a", toolCallId: "tool-a" }, + ]); + }); }); describe("Relay observe v1", () => { @@ -267,7 +326,10 @@ const args = process.argv.slice(2); calls.push(args); fs.writeFileSync(callsPath, JSON.stringify(calls)); const id = args[0] === "observe" && args[1] === "--panel" ? args[2] : args[1]; -if (args[0] === "observe" && state[id]) console.log(JSON.stringify(state[id])); +if (args[0] === "observe" && state[id]?.schemaVersion === 1) console.log(JSON.stringify(state[id])); +else if (args[0] === "observe" && state[id]) console.log(JSON.stringify({ usage: "relay init | status [id]", request: "JSON from --request file" })); +else if (args[0] === "status" && state[id]) console.log(JSON.stringify(state[id])); +else if (args[0] === "panel-result" && state[id]) console.log(JSON.stringify(state[id])); else if (args[0] === "cancel" && state[id]) console.log(JSON.stringify({id, status: "cancelling"})); else process.exit(1); `, @@ -362,14 +424,11 @@ persistence("Relay persisted observer and controls", (it) => { toolCallId: "tool-a", status: "completed", data: { - toolName: "mcp__relay__relay_delegate", + toolName: "mcp__plugin_relay-orchestrator_relay__relay_delegate", result: { type: "tool_result", tool_use_id: "tool-a", - content: encodeJson({ - content: [{ type: "text", text: "Worker dispatched" }], - structuredContent: { schemaVersion: 1, kind: "job", jobId, attempt: 1 }, - }), + content: [{ type: "text", text: encodeJson({ id: jobId, status: "queued" }) }], }, }, }; @@ -395,6 +454,55 @@ persistence("Relay persisted observer and controls", (it) => { effort: "high", cancellable: true, }); + const legacyJobId = "job-77777777-7777-4777-8777-777777777777"; + cli.setState({ + [jobId]: job, + [legacyJobId]: { + id: legacyJobId, + status: "running", + providerType: "codex", + model: "gpt-6-sol", + effort: "low", + createdAt: "2026-09-24T00:00:00.000Z", + updatedAt: "2026-09-24T00:00:01.000Z", + }, + }); + yield* bridge.recordToolResult( + toolEvent({ + toolName: "mcp__plugin_relay-orchestrator_relay__relay_delegate", + itemId: "legacy-tool", + turnId: "legacy-turn", + result: { + type: "tool_result", + content: [ + { + type: "text", + text: encodeJson({ + id: legacyJobId, + status: "queued", + providerType: "codex", + }), + }, + ], + }, + }), + ); + yield* bridge.reconcile; + const legacyRows = yield* sql` + SELECT turn_id AS turnId, payload_json AS payload + FROM projection_thread_activities + WHERE kind = 'task.started' + AND json_extract(payload_json, '$.taskId') = ${`relay:${legacyJobId}`} + `; + expect(legacyRows).toHaveLength(1); + expect(legacyRows[0]?.turnId).toBe("legacy-turn"); + expect(decodeJson(legacyRows[0]?.payload)).toMatchObject({ + taskId: `relay:${legacyJobId}`, + source: "relay", + model: "gpt-6-sol", + effort: "low", + toolUseId: "legacy-tool", + }); const wrongThread = yield* Effect.result( bridge.cancel(ThreadId.make("thread-b"), `relay:${jobId}`), ); @@ -412,7 +520,7 @@ persistence("Relay persisted observer and controls", (it) => { yield* unavailable.reconcile; yield* unavailable.reconcile; const idleRows = - yield* sql`SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable'`; + yield* sql`SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable' AND json_extract(payload_json, '$.taskId') = ${`relay:${jobId}`}`; expect(idleRows).toHaveLength(1); expect(decodeJson(idleRows[0]?.payload)).toMatchObject({ taskId: `relay:${jobId}`, @@ -442,6 +550,7 @@ persistence("Relay persisted observer and controls", (it) => { const repeatedOutage = yield* sql` SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable' + AND json_extract(payload_json, '$.taskId') = ${`relay:${jobId}`} ORDER BY sequence `; expect(repeatedOutage).toHaveLength(2); @@ -466,7 +575,7 @@ persistence("Relay persisted observer and controls", (it) => { }); yield* reconnected.reconcile; const completed = - yield* sql`SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.completed'`; + yield* sql`SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.completed' AND json_extract(payload_json, '$.taskId') = ${`relay:${jobId}`}`; expect(completed).toHaveLength(1); expect(decodeJson(completed[0]?.payload)).toMatchObject({ taskId: `relay:${jobId}`, @@ -515,6 +624,7 @@ persistence("Relay persisted observer and controls", (it) => { const pendingResume = yield* sql` SELECT turn_id AS turnId, payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.progress' AND activity_id LIKE 'relay-observer-unavailable:%' + AND json_extract(payload_json, '$.taskId') = ${`relay:${jobId}`} ORDER BY sequence DESC LIMIT 1 `; expect(pendingResume).toHaveLength(1); @@ -530,7 +640,7 @@ persistence("Relay persisted observer and controls", (it) => { cli.setState({ [jobId]: { ...job, attempt: 2, sequence: 1, status: "running" } }); yield* resumedBridge.reconcile; const resumed = - yield* sql`SELECT turn_id AS turnId, payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.started' ORDER BY created_at`; + yield* sql`SELECT turn_id AS turnId, payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.started' AND json_extract(payload_json, '$.taskId') = ${`relay:${jobId}`} ORDER BY created_at`; expect(resumed).toHaveLength(2); expect(decodeJson(resumed[1]?.payload)).toMatchObject({ taskId: `relay:${jobId}`, @@ -944,12 +1054,143 @@ persistence("Relay persisted observer and controls", (it) => { Effect.provideService(ServerEnvironment, fakeEnvironment), Effect.provideService(ThreadBackgroundLivenessService, liveness), ); + const callsBeforeBoot = cli.calls().length; yield* bootAfterChurn.reconcile; + const bootCalls = cli.calls().slice(callsBeforeBoot); + const observed = (id: string) => + bootCalls.findIndex((args) => args[0] === "observe" && args[1] === id); + expect(observed(legacyJobId)).toBeGreaterThan(-1); + expect(observed(completedPanelJobId)).toBeGreaterThan(-1); + expect(observed(completedPanelJobId)).toBeLessThan(observed(legacyJobId)); expect( cli .calls() .filter((args) => args[0] === "observe" && args[1]?.startsWith("job-00000000-")), ).toHaveLength(0); + + const missingId = "job-cccccccc-cccc-4ccc-8ccc-cccccccccccc"; + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES ( + ${`relay-binding:${missingId}`}, 'thread-a', 'turn-missing', 'info', + 'relay.binding', 'Relay worker bound', + ${encodeJson({ kind: "job", id: missingId, toolCallId: "tool-missing", environmentId: "test-environment" })}, + 9999, '2026-09-24T00:00:00.000Z' + ) + `; + const missingBridge = yield* makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, engine), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, liveness), + ); + yield* missingBridge.reconcile; + yield* missingBridge.reconcile; + yield* missingBridge.reconcile; + const missingRows = yield* sql` + SELECT payload_json AS payload FROM projection_thread_activities + WHERE kind = 'task.progress' AND json_extract(payload_json, '$.taskId') = ${`relay:${missingId}`} + `; + expect(missingRows).toHaveLength(1); + expect(decodeJson(missingRows[0]?.payload)).toMatchObject({ + status: "idle", + summary: "Relay observer unavailable", + cancellable: false, + }); + const missingPolls = cli + .calls() + .filter((args) => args[0] === "observe" && args[1] === missingId).length; + yield* missingBridge.reconcile; + expect( + cli.calls().filter((args) => args[0] === "observe" && args[1] === missingId), + ).toHaveLength(missingPolls); + + const legacyPanelId = "panel-88888888-8888-4888-8888-888888888888"; + const priorId = "job-99999999-9999-4999-8999-999999999999"; + const currentId = "job-aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; + const otherId = "job-bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"; + const legacyDate = "2026-09-24T00:00:01.000Z"; + const legacyJobs = [ + { + id: currentId, + status: "running", + panelId: legacyPanelId, + panelMemberIndex: 0, + providerType: "codex", + createdAt: legacyDate, + updatedAt: legacyDate, + }, + { + id: otherId, + status: "running", + panelId: legacyPanelId, + panelMemberIndex: 1, + providerType: "claude-alt", + createdAt: legacyDate, + updatedAt: legacyDate, + }, + ]; + cli.setState({ + [legacyPanelId]: { + id: legacyPanelId, + request: { members: [{}, {}] }, + jobs: legacyJobs, + previousJobs: [priorId], + unstarted: [], + complete: false, + createdAt: legacyDate, + }, + [priorId]: { + id: priorId, + status: "failed", + panelId: legacyPanelId, + panelMemberIndex: 0, + createdAt: legacyDate, + updatedAt: legacyDate, + }, + [currentId]: legacyJobs[0], + [otherId]: legacyJobs[1], + }); + yield* bootAfterChurn.recordToolResult( + toolEvent({ + toolName: "mcp__plugin_relay-orchestrator_relay__relay_panel", + itemId: "legacy-panel-tool", + turnId: "legacy-panel-turn", + result: { + type: "tool_result", + content: [ + { + type: "text", + text: encodeJson({ + id: legacyPanelId, + jobs: [currentId, otherId], + unstarted: [], + }), + }, + ], + }, + }), + ); + yield* bootAfterChurn.reconcile; + const legacyMemberId = `relay-panel:${legacyPanelId}:member:0`; + const legacyMember = yield* sql` + SELECT turn_id AS turnId, payload_json AS payload + FROM projection_thread_activities WHERE kind = 'task.started' + AND json_extract(payload_json, '$.taskId') = ${legacyMemberId} + `; + expect(legacyMember).toHaveLength(1); + expect(legacyMember[0]?.turnId).toBe("legacy-panel-turn"); + expect(decodeJson(legacyMember[0]?.payload)).toMatchObject({ + taskId: legacyMemberId, + attempt: 2_000_001, + parentAgentId: `relay-panel:${legacyPanelId}`, + }); + expect( + (yield* bootAfterChurn.cancel(ThreadId.make("thread-a"), legacyMemberId)).disposition, + ).toBe("cancel-requested"); + expect(cli.calls().some((args) => args[0] === "cancel" && args[1] === currentId)).toBe( + true, + ); }).pipe(Effect.ensuring(Effect.sync(() => cli.cleanup()))); }, ); diff --git a/apps/server/src/orchestration/RelayWorkerBridge.ts b/apps/server/src/orchestration/RelayWorkerBridge.ts index cd05b9e9b4..1671b3fea6 100644 --- a/apps/server/src/orchestration/RelayWorkerBridge.ts +++ b/apps/server/src/orchestration/RelayWorkerBridge.ts @@ -17,6 +17,7 @@ import { type OrchestrationThreadActivity, } from "@t3tools/contracts"; import * as Context from "effect/Context"; +import * as Clock from "effect/Clock"; import * as Effect from "effect/Effect"; import * as DateTime from "effect/DateTime"; import * as Layer from "effect/Layer"; @@ -154,7 +155,7 @@ function relayReceiptFromToolEvent(event: ProviderRuntimeEvent): RelayToolReceip return undefined; } const result = item?.result ?? data?.result; - const structured = relayStructuredResult(result); + const structured = relayStructuredResult(result) ?? relayLegacyResult(result); if (!structured || structured.schemaVersion !== 1) return undefined; const base = { threadId: event.threadId, @@ -193,6 +194,48 @@ function relayReceiptFromToolEvent(event: ProviderRuntimeEvent): RelayToolReceip return undefined; } +/** The installed Claude plugin still returns its job/panel record as text. */ +function relayLegacyResult(value: unknown): Record | undefined { + const object = record(value); + const content = object?.content; + if (!Array.isArray(content) || content.length > 4) return undefined; + const text = record(content[0])?.text; + if (typeof text !== "string" || text.length > MAX_MCP_RECEIPT_CODE_UNITS) return undefined; + try { + const parsed = record(JSON.parse(text)); + if (!parsed) return undefined; + if ( + typeof parsed.id === "string" && + JOB_ID.test(parsed.id) && + typeof parsed.status === "string" && + parsed.status !== "not_dispatched" + ) { + return { + schemaVersion: 1, + kind: "job", + jobId: parsed.id, + ...(nonNegativeInteger(parsed.attempt) !== undefined ? { attempt: parsed.attempt } : {}), + }; + } + if ( + typeof parsed.id === "string" && + PANEL_ID.test(parsed.id) && + Array.isArray(parsed.jobs) && + parsed.jobs.length <= 8 + ) { + return { + schemaVersion: 1, + kind: "panel", + panelId: parsed.id, + jobIds: parsed.jobs.filter((id): id is string => typeof id === "string" && JOB_ID.test(id)), + }; + } + } catch { + /* Invalid plugin text is not a dispatch receipt. */ + } + return undefined; +} + function relayStructuredResult(value: unknown, depth = 0): Record | undefined { if (depth > 4) return undefined; const object = record(value); @@ -280,6 +323,79 @@ export function parseObservation( return undefined; } +function legacyObservation( + value: unknown, + expectedId: string, + priorJobSlots: ReadonlyMap = new Map(), +): RelayJobObservation | RelayPanelObservation | undefined { + const object = record(value); + if (!object || object.id !== expectedId) return undefined; + if (JOB_ID.test(expectedId)) { + const attempt = nonNegativeInteger(object.attempt) ?? 1; + const sequence = Date.parse(String(object.updatedAt ?? object.createdAt)); + if (attempt < 1 || !Number.isSafeInteger(sequence) || typeof object.status !== "string") + return undefined; + return parseObservation( + { + schemaVersion: 1, + kind: "job", + id: expectedId, + attempt, + sequence, + status: object.status, + providerType: object.providerType, + model: object.actualModel ?? object.model, + effort: object.effort, + panelId: object.panelId, + panelMemberIndex: object.panelMemberIndex, + createdAt: object.createdAt, + updatedAt: object.updatedAt, + }, + expectedId, + ); + } + if ( + !PANEL_ID.test(expectedId) || + !Array.isArray(object.jobs) || + !Array.isArray(object.unstarted) || + object.jobs.length > 8 + ) + return undefined; + const request = record(object.request); + const requested = request?.members; + if (!Array.isArray(requested) || requested.length < 2 || requested.length > 8) return undefined; + const jobs = object.jobs.map(record); + const members = requested.map((_, index) => { + const prior = Array.isArray(object.previousJobs) ? object.previousJobs : []; + const slotAttempt = 1 + prior.filter((id) => priorJobSlots.get(id) === index).length; + const job = jobs.find((candidate) => candidate?.panelMemberIndex === index); + const jobId = typeof job?.id === "string" && JOB_ID.test(job.id) ? job.id : null; + return { + index, + slotAttempt, + state: jobId ? "started" : "unstarted", + jobId, + ...(jobId ? { job: legacyObservation(job, jobId) } : {}), + }; + }); + return parseObservation( + { + schemaVersion: 1, + kind: "panel", + id: expectedId, + createdAt: object.createdAt, + complete: object.complete === true, + members, + }, + expectedId, + ); +} + +function legacyObserveUnavailable(value: unknown): boolean { + const object = record(value); + return typeof object?.usage === "string" && typeof object.request === "string"; +} + function observationFingerprint(observation: RelayJobObservation | RelayPanelObservation): string { return NodeCrypto.createHash("sha256") .update(JSON.stringify(observation)) @@ -363,6 +479,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu const seen = new Map(); const currentPanelJobs = new Map>(); const failedObservations = new Map(); + const missingObservationRetryAt = new Map(); const unavailable = new Set(); const outageEpochs = new Map(); const pendingPanelDispatchPolls = new Map(); @@ -370,6 +487,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu string, { attempt: number; usage: RelayUsageRollup | undefined } >(); + const legacyPriorJobSlots = new Map>(); const append = Effect.fn("RelayWorkerBridge.append")(function* ( binding: RelayBinding, @@ -457,14 +575,14 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu }); const readBindingPage = Effect.fn("RelayWorkerBridge.readBindingPage")(function* ( - afterRowId: number, + beforeRowId: number, ) { const rows = yield* sql` SELECT rowid AS rowId, thread_id AS threadId, turn_id AS turnId, payload_json AS payload FROM projection_thread_activities - WHERE rowid > ${afterRowId} AND kind = 'relay.binding' + WHERE rowid < ${beforeRowId} AND kind = 'relay.binding' AND json_extract(payload_json, '$.environmentId') = ${environmentId} - ORDER BY rowid ASC LIMIT 256 + ORDER BY rowid DESC LIMIT 256 `; return rows.map((raw) => ({ rowId: nonNegativeInteger(record(raw)?.rowId) ?? 0, @@ -729,19 +847,57 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu }, ); - const runCli = Effect.fn("RelayWorkerBridge.runCli")(function* (args: ReadonlyArray) { + const runCli = Effect.fn("RelayWorkerBridge.runCli")(function* ( + args: ReadonlyArray, + timeout = 12_000, + ) { if (!enabled || !cliPath) return Option.none<{ stdout: string; stderr: string }>(); return yield* Effect.tryPromise({ try: () => execFile(process.execPath, [cliPath, ...args], { - timeout: 12_000, - maxBuffer: MAX_OBSERVE_BYTES, + timeout, + maxBuffer: args[0] === "panel-result" ? MAX_MCP_RECEIPT_CODE_UNITS : MAX_OBSERVE_BYTES, windowsHide: true, }), catch: () => undefined, }).pipe(Effect.option); }); + const parseLegacyCliObservation = Effect.fn("RelayWorkerBridge.parseLegacyCliObservation")( + function* (value: unknown, id: string) { + if (!PANEL_ID.test(id)) return legacyObservation(value, id); + const previousJobs = record(value)?.previousJobs; + if (!Array.isArray(previousJobs) || previousJobs.length > 64) return undefined; + const previousIds = previousJobs.filter( + (previousId): previousId is string => + typeof previousId === "string" && JOB_ID.test(previousId), + ); + if (previousIds.length !== previousJobs.length) return undefined; + const priorSlots = legacyPriorJobSlots.get(id) ?? new Map(); + const missing = previousIds.filter((previousId) => !priorSlots.has(previousId)); + // Old retry records are immutable. Cache resolved slots and bound an + // unavailable history lookup so a current panel cannot stall the sweep. + const resolved = yield* Effect.forEach( + missing, + (previousId) => + Effect.gen(function* () { + const result = yield* runCli(["status", previousId], 2_000); + const decoded = Option.isSome(result) ? decodeJson(result.value.stdout) : Option.none(); + const job = Option.isSome(decoded) ? record(decoded.value) : undefined; + const index = nonNegativeInteger(job?.panelMemberIndex); + return job?.id === previousId && job.panelId === id && index !== undefined && index <= 7 + ? ([previousId, index] as const) + : undefined; + }), + { concurrency: 8 }, + ); + if (resolved.some((entry) => entry === undefined)) return undefined; + for (const entry of resolved) if (entry) priorSlots.set(entry[0], entry[1]); + legacyPriorJobSlots.set(id, priorSlots); + return legacyObservation(value, id, priorSlots); + }, + ); + const readLatestTaskActivity = Effect.fn("RelayWorkerBridge.readLatestTaskActivity")(function* ( binding: RelayBinding, ) { @@ -964,7 +1120,35 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu const payload = latest.payload; const completedAttempt = nonNegativeInteger(payload?.attempt); const completedSequence = nonNegativeInteger(payload?.relaySequence); - if (completedAttempt === undefined || completedSequence === undefined) return; + if (completedAttempt === undefined || completedSequence === undefined) { + // A recovered receipt may outlive its Relay files. Retain the binding + // for a later return, but do not recheck every missing historical job + // on every sweep ahead of live workers. + if (!unavailable.has(binding.id)) { + yield* append( + binding, + "task.progress", + { + taskId, + title: binding.kind === "panel" ? "Relay panel" : "Relay worker", + attempt: 1, + relaySequence: 0, + outageEpoch: 1, + status: "idle", + summary: "Relay observer unavailable", + detail: "Relay observer unavailable", + ...bindingPayload(binding), + cancellable: false, + }, + "Relay observer unavailable", + `relay-observer-unavailable:${binding.id}:1`, + "1:0:1", + ); + unavailable.add(binding.id); + } + missingObservationRetryAt.set(binding.id, (yield* Clock.currentTimeMillis) + 60_000); + return; + } const pending = latest.kind === "task.completed" ? yield* pendingGenerationAfter(binding, latest.rowId, completedAttempt) @@ -972,6 +1156,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu if (latest.kind === "task.completed") { if (!pending) { active.delete(binding.id); + legacyPriorJobSlots.delete(binding.id); seen.delete(binding.id); failedObservations.delete(binding.id); unavailable.delete(binding.id); @@ -1010,16 +1195,27 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu }); const poll = Effect.fn("RelayWorkerBridge.poll")(function* (binding: RelayBinding) { + if ((missingObservationRetryAt.get(binding.id) ?? 0) > (yield* Clock.currentTimeMillis)) return; const result = yield* runCli( binding.kind === "job" ? ["observe", binding.id] : ["observe", "--panel", binding.id], ); - if (Option.isNone(result)) return yield* markObserverUnavailable(binding); - const parsed = decodeJson(result.value.stdout); - const observation = parseObservation( + const parsed = Option.isSome(result) ? decodeJson(result.value.stdout) : Option.none(); + let observation = parseObservation( Option.isSome(parsed) ? parsed.value : undefined, binding.id, ); + if (!observation && Option.isSome(parsed) && legacyObserveUnavailable(parsed.value)) { + const legacy = yield* runCli( + binding.kind === "job" ? ["status", binding.id] : ["panel-result", binding.id], + ); + const decoded = Option.isSome(legacy) ? decodeJson(legacy.value.stdout) : Option.none(); + observation = yield* parseLegacyCliObservation( + Option.isSome(decoded) ? decoded.value : undefined, + binding.id, + ); + } if (!observation) return yield* markObserverUnavailable(binding); + missingObservationRetryAt.delete(binding.id); const wasUnavailableInMemory = unavailable.delete(binding.id); const lastPersisted = seen.has(binding.id) ? undefined : yield* readLatestTaskActivity(binding); const wasUnavailable = @@ -1154,6 +1350,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu fingerprint, ); active.delete(binding.id); + legacyPriorJobSlots.delete(binding.id); seen.delete(binding.id); failedObservations.delete(binding.id); unavailable.delete(binding.id); @@ -1165,6 +1362,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu // idle panel. A new continuation or member resume receipt reactivates // observation; polling this unchanged snapshot forever is needless. active.delete(binding.id); + legacyPriorJobSlots.delete(binding.id); seen.delete(binding.id); failedObservations.delete(binding.id); unavailable.delete(binding.id); @@ -1304,12 +1502,14 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu // the crash window after command dispatch and before observer registration. if (!bootstrapped) { yield* recoverPersistedToolReceipts(); - let afterRowId = 0; + // Replay inserts historical bindings in receipt order. Visit newest + // bindings first so current workers project before old missing jobs. + let beforeRowId = Number.MAX_SAFE_INTEGER; for (;;) { - const page = yield* readBindingPage(afterRowId); + const page = yield* readBindingPage(beforeRowId); if (page.length === 0) break; for (const { rowId, binding } of page) { - afterRowId = Math.max(afterRowId, rowId); + beforeRowId = Math.min(beforeRowId, rowId); if (binding && !active.has(binding.id) && (yield* shouldObserveOnBoot(binding, rowId))) { active.set(binding.id, binding); } @@ -1404,13 +1604,25 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu ) return yield* new ProviderCancelSessionAgentError({ reason: "agent-not-active" }); const current = yield* runCli(["observe", "--panel", panelId]); - if (Option.isNone(current)) - return yield* new ProviderCancelSessionAgentError({ reason: "request-failed" }); - const decodedPanel = decodeJson(current.value.stdout); - const observedPanel = parseObservation( + const decodedPanel = Option.isSome(current) + ? decodeJson(current.value.stdout) + : Option.none(); + let observedPanel = parseObservation( Option.isSome(decodedPanel) ? decodedPanel.value : undefined, panelId, ); + if ( + !observedPanel && + Option.isSome(decodedPanel) && + legacyObserveUnavailable(decodedPanel.value) + ) { + const legacy = yield* runCli(["panel-result", panelId]); + const decoded = Option.isSome(legacy) ? decodeJson(legacy.value.stdout) : Option.none(); + observedPanel = yield* parseLegacyCliObservation( + Option.isSome(decoded) ? decoded.value : undefined, + panelId, + ); + } if (observedPanel?.kind !== "panel") return yield* new ProviderCancelSessionAgentError({ reason: "request-failed" }); const member = observedPanel.members.find((candidate) => candidate.index === index); @@ -1441,12 +1653,17 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu if (!binding) return yield* new ProviderCancelSessionAgentError({ reason: "agent-not-active" }); const id = binding.id; const observed = yield* runCli(["observe", id]); - if (Option.isNone(observed)) - return yield* new ProviderCancelSessionAgentError({ reason: "request-failed" }); - const decodedJob = decodeJson(observed.value.stdout); - if ( - parseObservation(Option.isSome(decodedJob) ? decodedJob.value : undefined, id)?.kind !== "job" - ) { + const decodedJob = Option.isSome(observed) ? decodeJson(observed.value.stdout) : Option.none(); + let current = parseObservation(Option.isSome(decodedJob) ? decodedJob.value : undefined, id); + if (!current && Option.isSome(decodedJob) && legacyObserveUnavailable(decodedJob.value)) { + const legacy = yield* runCli(["status", id]); + const decoded = Option.isSome(legacy) ? decodeJson(legacy.value.stdout) : Option.none(); + current = yield* parseLegacyCliObservation( + Option.isSome(decoded) ? decoded.value : undefined, + id, + ); + } + if (current?.kind !== "job") { return yield* new ProviderCancelSessionAgentError({ reason: "request-failed" }); } const result = yield* runCli(["cancel", id]); From 90129e7e41ae318c5ccffa3c080b0f9639f654a0 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:19:33 -0600 Subject: [PATCH 017/104] fix(relay): accept delegated thread IDs in activity routes (#756) --- .../migration.sql | 2 + .../snapshot.json | 1516 +++++++++++++++++ infra/relay/src/http/Api.test.ts | 127 +- infra/relay/src/http/Api.ts | 6 + infra/relay/src/persistence/schema.ts | 4 +- infra/relay/src/worker.ts | 2 + 6 files changed, 1646 insertions(+), 11 deletions(-) create mode 100644 infra/relay/migrations/postgres/20260918175607_long_thread_ids/migration.sql create mode 100644 infra/relay/migrations/postgres/20260918175607_long_thread_ids/snapshot.json diff --git a/infra/relay/migrations/postgres/20260918175607_long_thread_ids/migration.sql b/infra/relay/migrations/postgres/20260918175607_long_thread_ids/migration.sql new file mode 100644 index 0000000000..1bb8be0559 --- /dev/null +++ b/infra/relay/migrations/postgres/20260918175607_long_thread_ids/migration.sql @@ -0,0 +1,2 @@ +ALTER TABLE "relay_agent_activity_rows" ALTER COLUMN "thread_id" SET DATA TYPE varchar(512) USING "thread_id"::varchar(512);--> statement-breakpoint +ALTER TABLE "relay_delivery_attempts" ALTER COLUMN "thread_id" SET DATA TYPE varchar(512) USING "thread_id"::varchar(512); \ No newline at end of file diff --git a/infra/relay/migrations/postgres/20260918175607_long_thread_ids/snapshot.json b/infra/relay/migrations/postgres/20260918175607_long_thread_ids/snapshot.json new file mode 100644 index 0000000000..4e56b797b9 --- /dev/null +++ b/infra/relay/migrations/postgres/20260918175607_long_thread_ids/snapshot.json @@ -0,0 +1,1516 @@ +{ + "version": "8", + "dialect": "postgres", + "id": "4b6d0d21-8d78-4499-9dde-b42bcf633d05", + "prevIds": ["1109ed23-036c-4c39-a459-52422a7ffd99"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "relay_agent_activity_rows", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_delivery_attempts", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_dpop_proofs", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_environment_credentials", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_environment_links", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_live_activities", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_managed_endpoint_allocations", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_managed_tunnel_limits", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "relay_mobile_devices", + "entityType": "tables", + "schema": "public" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_id", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_public_key", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "varchar(512)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "state_json", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "type": "varchar(36)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(512)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thread_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "kind", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "source_job_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(16)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token_suffix", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "apns_status", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "apns_reason", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "apns_id", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transport_error", + "entityType": "columns", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "type": "varchar(128)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "thumbprint", + "entityType": "columns", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "jti", + "entityType": "columns", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "iat", + "entityType": "columns", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_id", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_public_key", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_hash", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "revoked_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_id", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'T3 Environment'", + "generated": null, + "identity": null, + "name": "environment_label", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_public_key", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "endpoint_http_base_url", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "endpoint_ws_base_url", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(32)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "endpoint_provider_kind", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "notifications_enabled", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "live_activities_enabled", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "managed_tunnels_enabled", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_by_device_id", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "revoked_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_environment_links" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_id", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "activity_push_token", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remote_start_queued_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remote_started_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ended_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_aggregate_json", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_live_activity_delivery_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_live_activities" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "environment_id", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "hostname", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tunnel_id", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "tunnel_name", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "dns_record_id", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ready_at", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "type": "varchar(191)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_tunnel_limits" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "max_tunnels", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_tunnel_limits" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_tunnel_limits" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_managed_tunnel_limits" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_id", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'iOS device'", + "generated": null, + "identity": null, + "name": "label", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(16)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "platform", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ios_major_version", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "android_api_level", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "app_version", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(255)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "bundle_id", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(16)", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aps_environment", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "push_token", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "push_to_start_token", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "preferences_json", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "type": "varchar(64)", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "updated_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_agent_activity_rows_updated", + "entityType": "indexes", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "environment_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "thread_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_delivery_attempts_environment", + "entityType": "indexes", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "source_job_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_delivery_attempts_source_job", + "entityType": "indexes", + "schema": "public", + "table": "relay_delivery_attempts" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_dpop_proofs_expires_at", + "entityType": "indexes", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "credential_hash", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_environment_credentials_hash", + "entityType": "indexes", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "environment_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "revoked_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_environment_credentials_environment", + "entityType": "indexes", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "environment_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "environment_public_key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "revoked_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_environment_credentials_environment_key", + "entityType": "indexes", + "schema": "public", + "table": "relay_environment_credentials" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "environment_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "revoked_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_environment_links_environment", + "entityType": "indexes", + "schema": "public", + "table": "relay_environment_links" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "activity_push_token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_live_activities_activity_push_token", + "entityType": "indexes", + "schema": "public", + "table": "relay_live_activities" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "hostname", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_managed_endpoint_allocations_hostname", + "entityType": "indexes", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "tunnel_name", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_managed_endpoint_allocations_tunnel_name", + "entityType": "indexes", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "push_token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_mobile_devices_push_token", + "entityType": "indexes", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "nameExplicit": true, + "columns": [ + { + "value": "push_to_start_token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": true, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "idx_relay_mobile_devices_push_to_start_token", + "entityType": "indexes", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "columns": ["environment_id", "environment_public_key", "thread_id"], + "nameExplicit": false, + "name": "relay_agent_activity_rows_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_agent_activity_rows" + }, + { + "columns": ["thumbprint", "jti"], + "nameExplicit": false, + "name": "relay_dpop_proofs_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_dpop_proofs" + }, + { + "columns": ["user_id", "environment_id"], + "nameExplicit": false, + "name": "relay_environment_links_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_environment_links" + }, + { + "columns": ["user_id", "device_id"], + "nameExplicit": false, + "name": "relay_live_activities_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_live_activities" + }, + { + "columns": ["user_id", "environment_id"], + "nameExplicit": false, + "name": "relay_managed_endpoint_allocations_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_managed_endpoint_allocations" + }, + { + "columns": ["user_id", "device_id"], + "nameExplicit": false, + "name": "relay_mobile_devices_pkey", + "entityType": "pks", + "schema": "public", + "table": "relay_mobile_devices" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "relay_delivery_attempts_pkey", + "schema": "public", + "table": "relay_delivery_attempts", + "entityType": "pks" + }, + { + "columns": ["credential_id"], + "nameExplicit": false, + "name": "relay_environment_credentials_pkey", + "schema": "public", + "table": "relay_environment_credentials", + "entityType": "pks" + }, + { + "columns": ["user_id"], + "nameExplicit": false, + "name": "relay_managed_tunnel_limits_pkey", + "schema": "public", + "table": "relay_managed_tunnel_limits", + "entityType": "pks" + } + ], + "renames": [] +} diff --git a/infra/relay/src/http/Api.test.ts b/infra/relay/src/http/Api.test.ts index 66b87e643a..bf7bd1f5a4 100644 --- a/infra/relay/src/http/Api.test.ts +++ b/infra/relay/src/http/Api.test.ts @@ -1,4 +1,15 @@ +import * as HttpServer from "effect/unstable/http/HttpServer"; +import { + RelayClientAuth, + RelayClientPrincipal, + type RelayClientDeviceRecord, +} from "@t3tools/contracts/relay"; +import * as EnvironmentLinker from "../environments/EnvironmentLinker.ts"; +import * as RelayTokens from "../auth/RelayTokens.ts"; +import * as Devices from "../agentActivity/Devices.ts"; import { createClerkClient, verifyToken } from "@clerk/backend"; +import * as NodeHttpPlatform from "@effect/platform-node/NodeHttpPlatform"; +import * as NodeServices from "@effect/platform-node/NodeServices"; import { describe, expect, it } from "@effect/vitest"; import { vi } from "vite-plus/test"; import * as Context from "effect/Context"; @@ -12,22 +23,21 @@ import * as Predicate from "effect/Predicate"; import * as Redacted from "effect/Redacted"; import * as TestClock from "effect/testing/TestClock"; import * as Tracer from "effect/Tracer"; +import * as Etag from "effect/unstable/http/Etag"; import * as HttpRouter from "effect/unstable/http/HttpRouter"; import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; -import * as HttpServer from "effect/unstable/http/HttpServer"; -import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as HttpApi from "effect/unstable/httpapi/HttpApi"; +import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import { EnvironmentId } from "@t3tools/contracts"; import { - RelayApi, - RelayClientAuth, - RelayClientPrincipal, RelayEnvironmentAuth, - type RelayClientDeviceRecord, + RelayEnvironmentPrincipal, + RelayApi, } from "@t3tools/contracts/relay"; import { + RELAY_HTTP_ROUTER_CONFIG, RELAY_REQUEST_DEADLINE_MS, clientApi, relayCors, @@ -36,6 +46,7 @@ import { relayNotFoundRoute, relayDpopFailureReason, revokeEnvironmentLinkRecord, + serverApi, traceRelayHttpRequestWith, unlinkEnvironmentRecord, verifyRelayClientBearerToken, @@ -46,9 +57,8 @@ import * as RelayDb from "../db.ts"; import * as EnvironmentCredentials from "../environments/EnvironmentCredentials.ts"; import * as EnvironmentLinks from "../environments/EnvironmentLinks.ts"; import * as ManagedEndpointProvider from "../environments/ManagedEndpointProvider.ts"; -import * as EnvironmentLinker from "../environments/EnvironmentLinker.ts"; -import * as RelayTokens from "../auth/RelayTokens.ts"; -import * as Devices from "../agentActivity/Devices.ts"; +import * as AgentActivityPublisher from "../agentActivity/AgentActivityPublisher.ts"; +import * as EnvironmentPublishSignatures from "../environments/EnvironmentPublishSignatures.ts"; vi.mock("@clerk/backend", () => ({ createClerkClient: vi.fn(), @@ -606,6 +616,105 @@ describe("relay request tracing", () => { }); describe("relay routing fallback", () => { + it.effect("publishes activity for escaped delegated thread IDs longer than 100 characters", () => + Effect.gen(function* () { + const environmentId = "environment-1"; + const environmentPublicKey = "environment-public-key"; + const published: Array< + Parameters[0] + > = []; + const verified: Array< + Parameters< + EnvironmentPublishSignatures.EnvironmentPublishSignatures["Service"]["verify"] + >[0] + > = []; + const publisher = Layer.succeed(AgentActivityPublisher.AgentActivityPublisher, { + publish: (input) => + Effect.sync(() => { + published.push(input); + return { ok: true as const, deliveries: [] }; + }), + replayForLiveActivityRegistration: () => Effect.succeed(null), + }); + const signatures = Layer.succeed(EnvironmentPublishSignatures.EnvironmentPublishSignatures, { + verify: (input) => + Effect.sync(() => { + verified.push(input); + }), + }); + const auth = Layer.succeed(RelayEnvironmentAuth, { + environmentBearer: (effect) => + effect.pipe( + Effect.provideService(RelayEnvironmentPrincipal, { + environmentId, + environmentPublicKey, + }), + ), + }); + const routes = HttpApiBuilder.layer( + HttpApi.make("RelayApi").add(RelayApi.groups.server), + ).pipe( + Layer.provide(serverApi.pipe(Layer.provide([publisher, signatures]))), + Layer.provide(auth), + Layer.provide([NodeServices.layer, NodeHttpPlatform.layer, Etag.layerWeak]), + ); + const httpEffect = yield* HttpRouter.toHttpEffect( + Layer.mergeAll(routes, relayNotFoundRoute, relayCors), + ).pipe(Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG)); + const threadIds = [ + "b7c8c522-d244-43dc-875f-7224fce79912", + "t".repeat(512), + `thread:${"t".repeat(505)}`, + "thread:delegated-task:command%3Amcp%3Ab7c8c522-d244-43dc-875f-7224fce79912%3Adelegate-task%3Agreet-subagent-20260813", + ]; + for (const threadId of threadIds) { + const request = HttpServerRequest.fromWeb( + new Request( + `https://relay.test/v1/environments/${environmentId}/threads/${encodeURIComponent(threadId)}/agent-activity`, + { + method: "POST", + headers: { + authorization: "Bearer environment-credential", + "content-type": "application/json", + }, + body: '{"state":null,"proof":"signed-proof"}', + }, + ), + ); + const response = yield* httpEffect.pipe( + Effect.provideService(HttpServerRequest.HttpServerRequest, request), + ); + expect(response.status).toBe(200); + } + const overLimit = HttpServerRequest.fromWeb( + new Request( + `https://relay.test/v1/environments/${environmentId}/threads/${"t".repeat(513)}/agent-activity`, + { + method: "POST", + headers: { + authorization: "Bearer environment-credential", + "content-type": "application/json", + }, + body: '{"state":null,"proof":"signed-proof"}', + }, + ), + ); + const overLimitResponse = yield* httpEffect.pipe( + Effect.provideService(HttpServerRequest.HttpServerRequest, overLimit), + ); + expect(overLimitResponse.status).toBe(404); + expect(published).toEqual( + threadIds.map((threadId) => ({ + environmentId, + environmentPublicKey, + threadId, + state: null, + })), + ); + expect(verified.map((input) => input.threadId)).toEqual(threadIds); + }).pipe(Effect.scoped), + ); + it.effect("redirects the relay root to the API docs", () => Effect.gen(function* () { const request = HttpServerRequest.fromWeb(new Request("https://relay.test/")); diff --git a/infra/relay/src/http/Api.ts b/infra/relay/src/http/Api.ts index fcaeca6404..5a4d0c9ca4 100644 --- a/infra/relay/src/http/Api.ts +++ b/infra/relay/src/http/Api.ts @@ -71,6 +71,12 @@ import * as MobileRegistrations from "../agentActivity/MobileRegistrations.ts"; import { withSpanAttributes } from "../observability.ts"; import * as RelayDb from "../db.ts"; +// Delegated thread IDs carry escaped command provenance and exceed the router's +// default 100-character path parameter limit. Match the environment server. +export const RELAY_HTTP_ROUTER_CONFIG = { + maxParamLength: 512, +} as const; + const relayCorsAllowedMethods = ["GET", "POST", "DELETE", "OPTIONS"] as const; const relayCorsAllowedHeaders = [ "authorization", diff --git a/infra/relay/src/persistence/schema.ts b/infra/relay/src/persistence/schema.ts index d9a7074bc0..fd766c7589 100644 --- a/infra/relay/src/persistence/schema.ts +++ b/infra/relay/src/persistence/schema.ts @@ -138,7 +138,7 @@ export const relayAgentActivityRows = pgTable( { environmentId: varchar("environment_id", { length: 191 }).notNull(), environmentPublicKey: text("environment_public_key").notNull(), - threadId: varchar("thread_id", { length: 191 }).notNull(), + threadId: varchar("thread_id", { length: 512 }).notNull(), stateJson: jsonb("state_json").notNull().$type(), updatedAt: varchar("updated_at", { length: 64 }).notNull(), createdAt: varchar("created_at", { length: 64 }).notNull(), @@ -156,7 +156,7 @@ export const relayDeliveryAttempts = pgTable( createdAt: varchar("created_at", { length: 64 }).notNull(), userId: varchar("user_id", { length: 255 }), environmentId: varchar("environment_id", { length: 191 }), - threadId: varchar("thread_id", { length: 191 }), + threadId: varchar("thread_id", { length: 512 }), deviceId: varchar("device_id", { length: 255 }), kind: varchar("kind", { length: 64 }).notNull(), sourceJobId: varchar("source_job_id", { length: 64 }), diff --git a/infra/relay/src/worker.ts b/infra/relay/src/worker.ts index da52da67c0..7cb05920e8 100644 --- a/infra/relay/src/worker.ts +++ b/infra/relay/src/worker.ts @@ -23,6 +23,7 @@ import { healthApi, metadataApi, mobileApi, + RELAY_HTTP_ROUTER_CONFIG, relayClientAuthLayer, relayDpopClientAuthLayer, relayCors, @@ -351,6 +352,7 @@ export const ApiLive = Api.make( relayNotFoundRoute, ).pipe( HttpRouter.toHttpEffect, + Effect.provideService(HttpRouter.RouterConfig, RELAY_HTTP_ROUTER_CONFIG), withoutCapturedParentSpan, Effect.flatMap((httpEffect) => traceRelayHttpRequestWith(httpEffect, relayTraceLayer)), ); From 29e57cfdb00f0685490660eacad93ac55b5312b3 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:19:36 -0600 Subject: [PATCH 018/104] feat(prime): show child progress notes in Agents view (#748) --- .../prime/PrimeAgentDaemonAdapter.test.ts | 42 ++++++++++++++++++- .../provider/prime/PrimeAgentDaemonAdapter.ts | 3 ++ .../prime/PrimeAgentDaemonEvents.test.ts | 20 +++++++++ .../provider/prime/PrimeAgentDaemonEvents.ts | 9 ++++ .../PrimeAgentDaemonRuntimeEvents.test.ts | 8 +++- .../prime/PrimeAgentDaemonRuntimeEvents.ts | 8 +++- .../src/state/subagentRuntime.test.ts | 23 ++++++++++ 7 files changed, 108 insertions(+), 5 deletions(-) diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.test.ts b/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.test.ts index ca3c2ae7d6..15db0b597b 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.test.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.test.ts @@ -6661,6 +6661,39 @@ describe("PrimeAgentDaemonAdapter", () => { }, }); } + yield* offer(captures, { + _tag: "ChildUpdated", + child: { + id: "child-private-progress", + parentId: "parent", + activeSessionId: "native-private-100", + model: "child-model", + label: "private-safe-label", + status: "running", + tokenCount: 10, + toolUseCount: 1, + activity: { kind: "executing", toolName: "bash" }, + progressNote: "Checking the failing test", + lastActivityAt: 1_000, + }, + }); + yield* awaitObservedType(subscription.observed, "task.progress"); + yield* offer(captures, { + _tag: "ChildUpdated", + child: { + id: "child-private-progress", + parentId: "parent", + activeSessionId: "native-private-100", + model: "child-model", + label: "private-safe-label", + status: "running", + tokenCount: 10, + toolUseCount: 1, + activity: { kind: "executing", toolName: "bash" }, + progressNote: "Checking the failing test", + lastActivityAt: 2_000, + }, + }); captures.agentRoster = [ { id: "child-private-progress", @@ -6676,6 +6709,7 @@ describe("PrimeAgentDaemonAdapter", () => { activity: { kind: "executing", toolName: "bash" }, answerPreview: "private-answer-roster", recap: "private-recap-roster", + progressNote: "Checking the failing test", }, ]; expect( @@ -6718,17 +6752,21 @@ describe("PrimeAgentDaemonAdapter", () => { yield* awaitObservedType(subscription.observed, "session.goal.updated"); const durableEvents = subscription.events.slice(durableStart); - expect(durableEvents.filter((event) => event.type === "task.progress")).toEqual([]); + expect(durableEvents.filter((event) => event.type === "task.progress")).toEqual([ + expect.objectContaining({ + payload: expect.objectContaining({ summary: "Checking the failing test" }), + }), + ]); const completed = durableEvents.filter((event) => event.type === "task.completed"); expect(completed).toHaveLength(1); expect(completed[0]).toMatchObject({ payload: { taskId: "child-private-progress", status: "failed", + summary: "Failed", typedUsage: { totalTokens: 12, toolUses: 1, durationMs: 1_500 }, }, }); - expect(completed[0]?.payload).not.toHaveProperty("summary"); const persisted = encodeUnknownJson(durableEvents); expect(persisted).not.toContain("private-answer"); expect(persisted).not.toContain("private-recap"); diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.ts b/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.ts index 18a0fb462b..3ecc76033b 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonAdapter.ts @@ -531,6 +531,7 @@ type PrimeAgentDaemonChildDurableProjection = { readonly messageable: boolean; readonly waiting: boolean; readonly lastToolName: string | undefined; + readonly progressNote: string | undefined; readonly tokenCount: number | undefined; readonly toolUseCount: number | undefined; readonly terminalDurationMs: number | undefined; @@ -560,6 +561,7 @@ function durableChildProjection( child.status === "running" && child.activity?.kind !== "waiting" ? durableChildText(child.activity?.toolName) : undefined, + progressNote: active ? durableChildText(child.progressNote) : undefined, tokenCount: durableChildCount(child.tokenCount), toolUseCount: durableChildCount(child.toolUseCount), terminalDurationMs: active ? undefined : durableChildCount(child.durationMs), @@ -581,6 +583,7 @@ function durableChildChanged( left.messageable !== right.messageable || left.waiting !== right.waiting || left.lastToolName !== right.lastToolName || + left.progressNote !== right.progressNote || left.tokenCount !== right.tokenCount || left.toolUseCount !== right.toolUseCount || left.terminalDurationMs !== right.terminalDurationMs diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonEvents.test.ts b/apps/server/src/provider/prime/PrimeAgentDaemonEvents.test.ts index 1a7e865f87..7829ea5431 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonEvents.test.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonEvents.test.ts @@ -819,6 +819,26 @@ describe("PrimeAgentDaemonEvents", () => { _tag: "ChildUpdated", child: { id: "child-1", status: "running", activity: { toolName: "ipython" } }, }); + expect( + decodePrimeAgentDaemonEvent( + sessionEvent({ + type: "rlm_child_update", + child: { + ...child, + progressNote: "Checking tests", + lastActivityAt: 1_700_000_000_000, + activityStaleMs: 600_000, + }, + }), + ), + ).toMatchObject({ + _tag: "ChildUpdated", + child: { + progressNote: "Checking tests", + lastActivityAt: 1_700_000_000_000, + activityStaleMs: 600_000, + }, + }); const queueChanged = decodePrimeAgentDaemonEvent( sessionEvent({ type: "session_action_update", actions }), ); diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonEvents.ts b/apps/server/src/provider/prime/PrimeAgentDaemonEvents.ts index de46966fe7..335895f01d 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonEvents.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonEvents.ts @@ -387,6 +387,9 @@ const rlmChild = Schema.Struct({ toolUseCount: Schema.optional(Schema.Number), tokenCount: Schema.optional(Schema.Number), recap: Schema.optional(Schema.String), + progressNote: Schema.optional(Schema.String), + lastActivityAt: Schema.optional(Schema.Finite), + activityStaleMs: Schema.optional(Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))), sessionDir: Schema.String, activity: Schema.optional( Schema.Struct({ @@ -1309,6 +1312,9 @@ export type PrimeDaemonEvent = ( readonly toolUseCount?: number | undefined; readonly tokenCount?: number | undefined; readonly recap?: string | undefined; + readonly progressNote?: string | undefined; + readonly lastActivityAt?: number | undefined; + readonly activityStaleMs?: number | undefined; readonly activity?: | { readonly kind: "waiting" | "writing" | "executing"; @@ -1656,6 +1662,9 @@ function mapChild( toolUseCount: value.toolUseCount, tokenCount: value.tokenCount, recap: optionalBounded(value.recap, MAX_PREVIEW_LENGTH), + progressNote: optionalBounded(value.progressNote, 512), + lastActivityAt: value.lastActivityAt, + activityStaleMs: value.activityStaleMs, activity: value.activity, error: optionalBounded(value.error, MAX_PREVIEW_LENGTH), }; diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.test.ts b/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.test.ts index a4f078db40..4b42987701 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.test.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.test.ts @@ -660,6 +660,9 @@ describe("mapPrimeAgentDaemonRuntimeEventDrafts", () => { tokenCount: 90, answerPreview: "private live answer", recap: "private recap", + progressNote: "Checking the failing test", + lastActivityAt: 1_700_000_000_000, + activityStaleMs: 600_000, error: "private transient error", activity: { kind: "executing", toolName: "bash" }, }, @@ -676,6 +679,7 @@ describe("mapPrimeAgentDaemonRuntimeEventDrafts", () => { taskId: RuntimeTaskId.make("child-1"), description: "Review tests", status: "running", + summary: "Checking the failing test", typedUsage: { totalTokens: 90, toolUses: 4, durationMs: 250 }, lastToolName: "bash", taskType: "subagent", @@ -693,6 +697,8 @@ describe("mapPrimeAgentDaemonRuntimeEventDrafts", () => { expect(JSON.stringify(running)).not.toContain("private live answer"); expect(JSON.stringify(running)).not.toContain("private recap"); expect(JSON.stringify(running)).not.toContain("private transient error"); + expect(JSON.stringify(running)).not.toContain("activityStaleMs"); + expect(JSON.stringify(running)).not.toContain("lastActivityAt"); const terminal = mapPrimeAgentDaemonRuntimeEventDrafts({ ...context, @@ -716,12 +722,12 @@ describe("mapPrimeAgentDaemonRuntimeEventDrafts", () => { payload: { taskId: RuntimeTaskId.make("child-1"), status: "failed", + summary: "Failed", typedUsage: { totalTokens: 100 }, messageable: false, timelineBypass: true, }, }); - expect(terminal[0]?.payload).not.toHaveProperty("summary"); expect(JSON.stringify(terminal)).not.toContain("private terminal"); }); diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.ts b/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.ts index b2912965b9..566f6f90c3 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonRuntimeEvents.ts @@ -307,7 +307,8 @@ function childDraft( }; case "running": { const lastToolName = boundedNonEmpty(child.activity?.toolName, MAX_SCALAR_LENGTH); - if (child.activity?.kind === "waiting") { + const progressNote = boundedNonEmpty(child.progressNote?.trim(), 512); + if (child.activity?.kind === "waiting" && progressNote === undefined) { return { ...runtimeBase(input), type: "task.updated", @@ -320,7 +321,8 @@ function childDraft( payload: { taskId, description, - status: "running", + status: child.activity?.kind === "waiting" ? "waiting" : "running", + ...(progressNote === undefined ? {} : { summary: progressNote }), ...(typedUsage === undefined ? {} : { typedUsage }), ...(lastToolName === undefined ? {} : { lastToolName }), ...linkage, @@ -337,6 +339,8 @@ function childDraft( taskId, status: child.status === "done" ? "completed" : child.status === "error" ? "failed" : "stopped", + summary: + child.status === "done" ? "Completed" : child.status === "error" ? "Failed" : "Stopped", ...(typedUsage === undefined ? {} : { typedUsage }), ...linkage, }, diff --git a/packages/client-runtime/src/state/subagentRuntime.test.ts b/packages/client-runtime/src/state/subagentRuntime.test.ts index ff066e045f..e1aa979c2e 100644 --- a/packages/client-runtime/src/state/subagentRuntime.test.ts +++ b/packages/client-runtime/src/state/subagentRuntime.test.ts @@ -71,6 +71,29 @@ function fold(rows: ReadonlyArray) { } describe("foldSubagentActivities", () => { + it("shows a Prime child note while active and its outcome after completion", () => { + const [agent] = fold([ + activity("task.progress", { + taskId: "prime-child-1", + taskType: "subagent", + title: "Review tests", + status: "running", + summary: "Checking the failing test", + }), + activity("task.completed", { + taskId: "prime-child-1", + taskType: "subagent", + status: "completed", + summary: "Completed", + }), + ]); + expect(agent).toMatchObject({ + status: "completed", + progress: "Checking the failing test", + result: "Completed", + }); + }); + it("shows the batch status limit after its parent turn ends without claiming a result", () => { const running = activity("task.progress", { taskId: "batch-1", From ccb8854cb8d511e9590569229c553063eb337a8b Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:34:10 -0600 Subject: [PATCH 019/104] fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row --- .agents/upstream-review.md | 1 + .../src/provider/Drivers/ClaudeDriver.ts | 1 + .../src/provider/Drivers/CursorDriver.ts | 5 +- .../provider/Layers/CursorProvider.test.ts | 6 +- .../src/provider/Layers/CursorProvider.ts | 7 +- .../server/src/provider/ModelManifest.test.ts | 78 +++++++++++++ apps/server/src/provider/ModelManifest.ts | 10 +- apps/server/src/provider/ProviderDriver.ts | 2 + apps/server/src/server.test.ts | 103 ++++++++++++++++++ apps/server/src/ws.ts | 27 +++++ packages/contracts/src/rpc.ts | 3 +- 11 files changed, 234 insertions(+), 9 deletions(-) diff --git a/.agents/upstream-review.md b/.agents/upstream-review.md index ac1894ec4b..a9411b334f 100644 --- a/.agents/upstream-review.md +++ b/.agents/upstream-review.md @@ -35,6 +35,7 @@ Historical groups are indexed in the linked archive. This file migration changes | Group / bounded head | Sources | Outcome and remaining scope | Pylon PR / verification | | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Explicit provider refresh cache bypass / `f193a6863c494fdfa36a1ef3132e42a7d7b42926` | `f193a6863c494fdfa36a1ef3132e42a7d7b42926` (#13109) | Implemented in PR #743, pending merge: explicit refresh bypasses the model manifest freshness/retry timer and provider-owned capability, model and package-version caches before status probes. Background probes retain their timers; Pylon's installed-provider `refreshModels` path remains. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #743](https://github.com/pylon-code/pylon/pull/743); independent adversarial review, 246 focused tests, server typecheck and scoped lint/format. | | OTLP log export, desktop main process telemetry, and per-signal observability / `fa1e1715538f6f8ac076761f7821f68479f616ab` | `3bb06ad910389fcfc886bd5156f41dc73f307f80` (#12493), `82059df1523d070b00ab6f45159115f626a4977e` (#12520), `a8693eb4ed3a66d72e6b814117e7beeaf0fb5f8b` (#12540), `fa1e1715538f6f8ac076761f7821f68479f616ab` (#12657) | Adopted all four in Cycle #689 (PR #690). Server exports structured log records over OTLP with trace/span ID correlation, replaces `tracerLogger` to eliminate duplicate in-span span-event exports, and applies dedicated flusher/serialization layers. Desktop exports main process traces and logs over OTLP while withholding metric emissions until metrics are instrumented. OTLP settings and HTTP header sets are isolated per signal (traces, metrics, logs) across server and desktop configurations. Adversarial review findings resolved: preserved `pylon-server` runtime and service identity defaults, verified `DEFAULT_SIGNAL_EXPORT` fallback, tested backward compatibility of `ServerObservability` schema decoding, and validated log deduplication. Cursor unchanged. | Cycle [#689](https://github.com/pylon-code/pylon/issues/689), [PR #690](https://github.com/pylon-code/pylon/pull/690); independent adversarial review, new ServerLogger test suite, 9 desktop observability tests, all CI checks green. | | Web chat/diff polish, editor discovery, diff cache eviction, ACP elicitation, and preview recovery / `c14f6015bfe479d313355cb234af1a5c16dbb15f` | `c14f6015bfe479d313355cb234af1a5c16dbb15f` (#12453), `0ff87f251dafb32703d5f531e7b248ad0a581ee2` (#10831), `4a560b4e4ebb37efb7f57805ba79e37f5500bdca` (#12438), `9accc567670da5096e9a694c1adb0d35adb51d9d` (#12571), `dfbb11bdd7c3f1a5575cb55d3e3abb12be025727` (#12377), `408ff8ae9bd7eb2e7e90cbfd8b3fcfe63641bf23` (#12439), `cb3d95c17487f3d834a5537f7c3aa0106bc9b5ec` (#12523), `de6a230db0bb71d3fa91d8de0ae7067b68253f82` (#11294), `efb96939fbb135b9f4de3bff19447200b178b23a` (#7861), `5378f87f99175bded2b9241876319567875aca03` (#12535) | Adopted all ten across Cycle #685 Group A (PR #686) and Group B (PR #687). Retained whitespace when copying diff contents, kept linebreaks in citation content, opened chat popovers with relative placement, discovered installed external editors on desktop outside PATH, wrapped long titles in confirmation dialogs, showed plain text in collapsed thought previews, released oversized pull request diff cache entries, accepted ACP SDK elicitation requests (`session/elicitation` & `elicitation/create`), surfaced Codex app permission requests as approvable, and recovered preview host registration after request timeouts via connection queue eviction. Hardening and adversarial review findings resolved: normalized `acceptAlways` approval decision in Codex session runtime to grant permissions, enforced retirement epoch and quarantine boundaries on approval requests, scoped event emission with `CodexNotificationEpoch`, wrapped diff cache invalidation in `Effect.uninterruptible`, cleaned up mobile icon maps, and rebranded pre-existing PR tool descriptions to Pylon. Preserved Pylon Effect services, session lifecycles, and UI layout authority. Cursor unchanged. | Cycle [#685](https://github.com/pylon-code/pylon/issues/685), [PR #686](https://github.com/pylon-code/pylon/pull/686) and [PR #687](https://github.com/pylon-code/pylon/pull/687); independent adversarial reviews, 260+ focused unit tests across web, server, and effect-acp, full workspace typecheck, scoped format/lint. | | Web interaction, ACP session startup stderr, Claude continuation, and GTK4 snapshot / `52d08a14b9e475371658fabbb12c056c3d3dad67` | `8dd02470b1e7603b8d36a21ae27c510480351f18` (#12552), `7445aa733ada33e45289e5aa5055f79142556513` (#11263), `599c9776eb887d4fff42da7d2daf8aa07ee9a9e2` (#11268), `dcf8942304cfef382af55c0068cb2f04c86757fc` (#12636), `e36725682bd2935f1bcfdf81660cb569b5f3df29` (#12577), `63ff33756c47f127391962458d8388a647e8ed44` (#12624), `d6f291303ddc0c9a14f570266a4d9eff6d431593` (#12625), `52d08a14b9e475371658fabbb12c056c3d3dad67` (#12635) | Adopted all eight across Cycle #682 Group A (PR #683) and Group B (PR #684). Preserved draft question input when clicking options, desktop CSP screenshot annotations, restored providers settings heading, aligned PR detail menu glyphs. Empty Claude homePath correctly resolves and shares continuation with `~/.claude` or `CLAUDE_CONFIG_DIR`, surfaced ACP stderr excerpt in session startup failures instead of generic closed session errors, and extracted desktop accessible text for Flatpak and GTK4 apps in SnapShot. Adversarial review findings resolved: ACP stderr excerpt sanitized against tokens/credentials and pending buffer bounded; defensive empty `homePath` trimming and `CLAUDE_CONFIG_DIR` resolution in OAuth usage; desktop accessibility timeout preserved without premature concurrency overlap. Preserved Pylon provider rate limits and error reporting. Cursor unchanged. | Cycle [#682](https://github.com/pylon-code/pylon/issues/682), [PR #683](https://github.com/pylon-code/pylon/pull/683) and [PR #684](https://github.com/pylon-code/pylon/pull/684); independent adversarial reviews, 225+ focused tests across desktop and server, full workspace typecheck, scoped format/lint. | diff --git a/apps/server/src/provider/Drivers/ClaudeDriver.ts b/apps/server/src/provider/Drivers/ClaudeDriver.ts index cbcf626858..291589e252 100644 --- a/apps/server/src/provider/Drivers/ClaudeDriver.ts +++ b/apps/server/src/provider/Drivers/ClaudeDriver.ts @@ -333,6 +333,7 @@ export const ClaudeDriver: ProviderDriver = { accentColor, enabled, snapshot, + invalidateCaches: Cache.invalidateAll(capabilitiesProbeCache), snapshotForCwd, adapter, textGeneration, diff --git a/apps/server/src/provider/Drivers/CursorDriver.ts b/apps/server/src/provider/Drivers/CursorDriver.ts index 5466af802e..8715dd9835 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.ts @@ -137,11 +137,11 @@ export const CursorDriver: ProviderDriver = { }); const textGeneration = yield* makeCursorTextGeneration(effectiveConfig, processEnv); - const discoverModels = yield* makeCursorModelDiscovery(effectiveConfig, processEnv); + const modelDiscovery = yield* makeCursorModelDiscovery(effectiveConfig, processEnv); const checkProvider = checkCursorProviderStatus( effectiveConfig, processEnv, - discoverModels, + modelDiscovery.discover, ).pipe( Effect.map(stampIdentity), Effect.provideService(Crypto.Crypto, crypto), @@ -196,6 +196,7 @@ export const CursorDriver: ProviderDriver = { accentColor, enabled, snapshot, + invalidateCaches: modelDiscovery.invalidate, snapshotForCwd: (cwd) => !effectiveConfig.enabled ? snapshot.getSnapshot diff --git a/apps/server/src/provider/Layers/CursorProvider.test.ts b/apps/server/src/provider/Layers/CursorProvider.test.ts index 7337a19010..dd314b0ec9 100644 --- a/apps/server/src/provider/Layers/CursorProvider.test.ts +++ b/apps/server/src/provider/Layers/CursorProvider.test.ts @@ -626,7 +626,7 @@ describe("discoverCursorModelsViaAcp", () => { apiEndpoint: "", customModels: [], }; - const discover = yield* makeCursorModelDiscovery(settings, { + const { discover, invalidate } = yield* makeCursorModelDiscovery(settings, { ...process.env, T3_ACP_REQUEST_LOG_PATH: requestLogPath, }); @@ -639,6 +639,10 @@ describe("discoverCursorModelsViaAcp", () => { yield* fileSystem.writeFileString(requestLogPath, ""); expect(yield* discover(about)).toEqual(first); expect(yield* fileSystem.readFileString(requestLogPath)).toBe(""); + yield* invalidate; + expect(yield* discover(about)).toEqual(first); + expect(yield* fileSystem.readFileString(requestLogPath)).toContain("initialize"); + yield* fileSystem.writeFileString(requestLogPath, ""); yield* discover({ ...about, version: "2026.08.12" }); expect(yield* fileSystem.readFileString(requestLogPath)).toContain("initialize"); yield* fileSystem.writeFileString(requestLogPath, ""); diff --git a/apps/server/src/provider/Layers/CursorProvider.ts b/apps/server/src/provider/Layers/CursorProvider.ts index 1d80fa20b8..e46989ab62 100644 --- a/apps/server/src/provider/Layers/CursorProvider.ts +++ b/apps/server/src/provider/Layers/CursorProvider.ts @@ -588,8 +588,11 @@ export const makeCursorModelDiscovery = Effect.fn("makeCursorModelDiscovery")(fu Exit.isSuccess(exit) && exit.value.length > 0 ? Duration.minutes(30) : Duration.zero, }, ); - return (about: Pick) => - Cache.get(cache, JSON.stringify([about.version, about.auth])); + return { + discover: (about: Pick) => + Cache.get(cache, JSON.stringify([about.version, about.auth])), + invalidate: Cache.invalidateAll(cache), + }; }); function getCursorFallbackModels( diff --git a/apps/server/src/provider/ModelManifest.test.ts b/apps/server/src/provider/ModelManifest.test.ts index f779280e6a..8f8e6b4d56 100644 --- a/apps/server/src/provider/ModelManifest.test.ts +++ b/apps/server/src/provider/ModelManifest.test.ts @@ -363,6 +363,84 @@ describe("ModelManifest service", () => { ); }); + it.live("explicit refresh bypasses fresh memory and disk caches", () => { + let fetchCount = 0; + const updated: ModelManifestData = { + ...REMOTE_MANIFEST, + currentModels: { codex: ["gpt-reloaded"] }, + }; + return Effect.gen(function* () { + const service = yield* make; + assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); + assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); + assert.strictEqual(fetchCount, 1); + + const rebooted = yield* make; + assert.deepStrictEqual(yield* rebooted.refresh, REMOTE_MANIFEST); + assert.strictEqual(fetchCount, 1); + assert.deepStrictEqual(yield* rebooted.forceRefresh, updated); + assert.strictEqual(fetchCount, 2); + assert.deepStrictEqual(yield* rebooted.current, updated); + assert.deepStrictEqual(yield* (yield* make).current, updated); + }).pipe( + Effect.scoped, + Effect.provide( + serviceLayers({ + prefix: "model-manifest-force-refresh-test", + response: () => Response.json(fetchCount++ === 0 ? REMOTE_MANIFEST : updated), + }), + ), + ); + }); + + it.live("explicit refresh retries immediately after failure and preserves last-good data", () => { + let fetchCount = 0; + return Effect.gen(function* () { + const service = yield* make; + assert.deepStrictEqual(yield* service.refresh, REMOTE_MANIFEST); + assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); + assert.deepStrictEqual(yield* service.current, REMOTE_MANIFEST); + assert.deepStrictEqual(yield* (yield* make).current, REMOTE_MANIFEST); + assert.strictEqual(fetchCount, 2); + assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); + assert.strictEqual(fetchCount, 3); + }).pipe( + Effect.scoped, + Effect.provide( + serviceLayers({ + prefix: "model-manifest-force-retry-test", + response: () => + fetchCount++ === 1 + ? new Response(null, { status: 503 }) + : Response.json(REMOTE_MANIFEST), + }), + ), + ); + }); + + it.live("explicit refresh bypasses the retry delay after an initial failure", () => { + let fetchCount = 0; + return Effect.gen(function* () { + const service = yield* make; + assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); + assert.deepStrictEqual(yield* service.refresh, BUNDLED_MODEL_MANIFEST); + assert.strictEqual(fetchCount, 1); + assert.deepStrictEqual(yield* service.forceRefresh, REMOTE_MANIFEST); + assert.strictEqual(fetchCount, 2); + }).pipe( + Effect.scoped, + Effect.provide( + serviceLayers({ + prefix: "model-manifest-force-initial-retry-test", + response: () => + fetchCount++ === 0 + ? new Response(null, { status: 503 }) + : Response.json(REMOTE_MANIFEST), + }), + ), + ); + }); + it.live("prefers a fetched manifest over the bundle and caches it to disk", () => Effect.gen(function* () { const service = yield* make; diff --git a/apps/server/src/provider/ModelManifest.ts b/apps/server/src/provider/ModelManifest.ts index bcf9ccfa8b..0ba775e231 100644 --- a/apps/server/src/provider/ModelManifest.ts +++ b/apps/server/src/provider/ModelManifest.ts @@ -341,6 +341,8 @@ export class ModelManifest extends Context.Service< readonly current: Effect.Effect; /** Manifest after a TTL-gated remote refresh; never fails. */ readonly refresh: Effect.Effect; + /** Explicit refresh bypasses freshness and retry timers, retaining last-good data. */ + readonly forceRefresh: Effect.Effect; /** Forks `refresh` into the service's own scope. Drivers call this from * provider checks: the fetch is process-shared state, so it must survive * the teardown of whichever instance happened to trigger it. */ @@ -352,6 +354,7 @@ export class ModelManifest extends Context.Service< const BundledOnlyModelManifest: ModelManifest["Service"] = { current: Effect.succeed(BUNDLED_MODEL_MANIFEST), refresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), + forceRefresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), refreshInBackground: Effect.void, }; @@ -388,7 +391,7 @@ export const make = Effect.gen(function* () { }), ); - const refresh = Effect.fn("ModelManifest.refresh")(function* () { + const refresh = Effect.fn("ModelManifest.refresh")(function* (force = false) { yield* ensureDiskCacheLoaded; const now = yield* Clock.currentTimeMillis; // A timestamp in the future means the wall clock moved backwards (the @@ -396,8 +399,8 @@ export const make = Effect.gen(function* () { // it as expired: the refetch rewrites both timestamps and self-heals. const isWithin = (sinceMs: number | null, windowMs: number) => sinceMs !== null && now >= sinceMs && now - sinceMs < windowMs; - if (isWithin(fetchedAtMs, MANIFEST_TTL_MS)) return manifest; - if (isWithin(lastAttemptMs, MANIFEST_RETRY_MS)) return manifest; + if (!force && isWithin(fetchedAtMs, MANIFEST_TTL_MS)) return manifest; + if (!force && isWithin(lastAttemptMs, MANIFEST_RETRY_MS)) return manifest; // The same switch that gates provider CLI update checks. It stops network // fetches only: a manifest already cached on disk from an earlier fetch @@ -450,6 +453,7 @@ export const make = Effect.gen(function* () { return ModelManifest.of({ current: ensureDiskCacheLoaded.pipe(Effect.map(() => manifest)), refresh: guardedRefresh, + forceRefresh: refreshSemaphore.withPermits(1)(refresh(true)), refreshInBackground: Effect.forkIn(guardedRefresh, serviceScope).pipe(Effect.asVoid), }); }); diff --git a/apps/server/src/provider/ProviderDriver.ts b/apps/server/src/provider/ProviderDriver.ts index 46b0f1cf80..2ca4fedcf7 100644 --- a/apps/server/src/provider/ProviderDriver.ts +++ b/apps/server/src/provider/ProviderDriver.ts @@ -84,6 +84,8 @@ export interface ProviderInstance { readonly enabled: boolean; readonly snapshot: ServerProviderShape; readonly snapshotForCwd?: (cwd: string) => Effect.Effect; + /** Invalidate provider-owned discovery caches before an explicit refresh. */ + readonly invalidateCaches?: Effect.Effect; readonly adapter: ProviderAdapterShape; readonly textGeneration: TextGeneration.TextGeneration["Service"]; readonly refreshModels?: () => Effect.Effect; diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index e3eb8af1cb..75ba2db39f 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -142,6 +142,12 @@ import { OrchestrationEventStore } from "./persistence/Services/OrchestrationEve import { RollbackSagaRepository } from "./persistence/Services/RollbackSagas.ts"; import { PersistenceSqlError } from "./persistence/Errors.ts"; import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; +import * as ModelManifest from "./provider/ModelManifest.ts"; +import { + ProviderVersionCache, + type ProviderVersionCacheEntry, +} from "./provider/providerMaintenance.ts"; +import type { ProviderInstance } from "./provider/ProviderDriver.ts"; import { ProviderAdapterUnsupportedOperationError, ProviderValidationError, @@ -506,6 +512,8 @@ const buildAppUnderTest = (options?: { antigravityInstallation?: Partial; environmentTheme?: Partial; providerRegistry?: Partial; + modelManifest?: Partial; + providerVersionCache?: Map; providerService?: Partial; rollbackSagaRepository?: Partial; serverSettings?: Partial; @@ -767,6 +775,14 @@ const buildAppUnderTest = (options?: { getInstance: () => Effect.succeed(undefined), ...options?.layers?.providerInstances, }), + Layer.mock(ModelManifest.ModelManifest)({ + current: Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), + refresh: Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), + forceRefresh: Effect.succeed(ModelManifest.BUNDLED_MODEL_MANIFEST), + refreshInBackground: Effect.void, + ...options?.layers?.modelManifest, + }), + Layer.succeed(ProviderVersionCache, options?.layers?.providerVersionCache ?? new Map()), Layer.mock(EnvironmentTheme.EnvironmentThemeService)({ current: Effect.succeed([]), streamChanges: Stream.empty, @@ -6428,6 +6444,93 @@ it.layer(NodeServices.layer)("server router seam", (it) => { }).pipe(Effect.provide(NodeHttpServer.layerTest)), ); + for (const mode of ["all", "targeted", "background"] as const) { + it.effect(`provider refresh invalidates owned caches before probing (${mode})`, () => { + const driver = ProviderDriverKind.make("codex"); + const instanceIds = [ProviderInstanceId.make("codex"), ProviderInstanceId.make("codex_work")]; + const packageNames = ["@example/personal", "@example/work"]; + const versionCache = new Map( + packageNames.map((name) => [ + name, + { expiresAt: Number.MAX_SAFE_INTEGER, version: "1.0.0" }, + ]), + ); + const invalidated: string[] = []; + const freshMaintenance: string[] = []; + let manifestRefreshed = false; + let probed = false; + const instances = instanceIds.map( + (instanceId, index) => + ({ + instanceId, + driverKind: driver, + continuationIdentity: { driverKind: driver, continuationKey: instanceId }, + displayName: undefined, + enabled: true, + invalidateCaches: Effect.sync(() => { + invalidated.push(instanceId); + }), + snapshot: { + resolveMaintenance: (options) => + Effect.sync(() => { + assert.isTrue(options?.fresh); + freshMaintenance.push(instanceId); + return makeManualOnlyProviderMaintenanceCapabilities({ + provider: driver, + packageName: packageNames[index]!, + }); + }), + getSnapshot: Effect.never, + refresh: Effect.never, + streamChanges: Stream.empty, + }, + adapter: {} as ProviderInstance["adapter"], + textGeneration: {} as ProviderInstance["textGeneration"], + }) satisfies ProviderInstance, + ); + const expected = + mode === "background" ? [] : mode === "targeted" ? [instanceIds[1]!] : instanceIds; + const probe = Effect.sync(() => { + probed = true; + assert.equal(manifestRefreshed, mode !== "background"); + assert.deepEqual(invalidated.toSorted(), expected.toSorted()); + assert.deepEqual(freshMaintenance.toSorted(), expected.toSorted()); + for (let index = 0; index < instanceIds.length; index++) { + assert.equal( + versionCache.has(packageNames[index]!), + !expected.includes(instanceIds[index]!), + ); + } + return []; + }); + return Effect.gen(function* () { + yield* buildAppUnderTest({ + layers: { + modelManifest: { + forceRefresh: Effect.sync(() => { + manifestRefreshed = true; + return ModelManifest.BUNDLED_MODEL_MANIFEST; + }), + }, + providerVersionCache: versionCache, + providerInstances: { listInstances: Effect.succeed(instances) }, + providerRegistry: { refresh: () => probe, refreshInstance: () => probe }, + }, + }); + const wsUrl = yield* getWsServerUrl("/ws"); + yield* Effect.scoped( + withWsRpcClient(wsUrl, (client) => + client[WS_METHODS.serverRefreshProviders]({ + ...(mode === "targeted" ? { instanceId: instanceIds[1]! } : {}), + ...(mode !== "background" ? { refreshModels: true } : {}), + }), + ), + ); + assert.isTrue(probed); + }).pipe(Effect.provide(NodeHttpServer.layerTest)); + }); + } + it.effect("serves config on reconnect without starting provider probes", () => Effect.gen(function* () { const refresh = vi.fn(() => Effect.never); diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index 9d4328a8cc..92bdd6e753 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -121,6 +121,8 @@ import { observeRpcStreamEffect as instrumentRpcStreamEffect, } from "./observability/RpcInstrumentation.ts"; import * as ProviderRegistry from "./provider/Services/ProviderRegistry.ts"; +import * as ModelManifest from "./provider/ModelManifest.ts"; +import * as ProviderMaintenance from "./provider/providerMaintenance.ts"; import * as ProviderService from "./provider/Services/ProviderService.ts"; import * as ProviderSessionDirectory from "./provider/Services/ProviderSessionDirectory.ts"; import * as ProviderMaintenanceRunner from "./provider/providerMaintenanceRunner.ts"; @@ -560,6 +562,8 @@ const makeWsRpcLayer = ( const deviceService = yield* DeviceService.DeviceService; const portDiscovery = yield* PortScanner.PortDiscovery; const providerRegistry = yield* ProviderRegistry.ProviderRegistry; + const modelManifest = yield* ModelManifest.ModelManifest; + const providerVersionCache = yield* ProviderMaintenance.ProviderVersionCache; const providerService = yield* ProviderService.ProviderService; const relayWorkerBridge = Option.getOrUndefined( yield* Effect.serviceOption(RelayWorkerBridge), @@ -2345,6 +2349,29 @@ const makeWsRpcLayer = ( observeRpcEffect( WS_METHODS.serverRefreshProviders, Effect.gen(function* () { + if (input.refreshModels) { + // Explicit refresh bypasses Pylon-owned caches; background probes + // keep their normal freshness windows. + yield* modelManifest.forceRefresh; + const instances = yield* providerInstances.listInstances; + yield* Effect.forEach( + instances.filter( + (instance) => + input.instanceId === undefined || input.instanceId === instance.instanceId, + ), + (instance) => + Effect.gen(function* () { + yield* instance.invalidateCaches ?? Effect.void; + const maintenance = yield* instance.snapshot.resolveMaintenance({ + fresh: true, + }); + if (maintenance.packageName) { + providerVersionCache.delete(maintenance.packageName); + } + }), + { concurrency: "unbounded", discard: true }, + ); + } let providers = yield* input.cwd !== undefined && input.instanceId !== undefined ? providerRegistry.refreshWorkspaceSnapshot({ instanceId: input.instanceId, diff --git a/packages/contracts/src/rpc.ts b/packages/contracts/src/rpc.ts index 1d481cc512..5580d8a67a 100644 --- a/packages/contracts/src/rpc.ts +++ b/packages/contracts/src/rpc.ts @@ -567,7 +567,8 @@ const WsServerRefreshProvidersRpc = Rpc.make(WS_METHODS.serverRefreshProviders, */ instanceId: Schema.optional(ProviderInstanceId), cwd: Schema.optional(TrimmedNonEmptyString), - /** Explicit user request. Background status refreshes must not open agent sessions. */ + /** Explicit user request: bypass provider discovery caches and rediscover models. + * Background status refreshes must not open agent sessions. */ refreshModels: Schema.optional(Schema.Boolean), }), success: ServerProviderUpdatedPayload, From 91e5166467536c5707caf236b395c9530524763a Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:34:14 -0600 Subject: [PATCH 020/104] fix(git): prevent stale ref checkout from restoring files (#751) --- apps/server/src/vcs/GitVcsDriverCore.test.ts | 53 ++++++++++++++++++++ apps/server/src/vcs/GitVcsDriverCore.ts | 3 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/server/src/vcs/GitVcsDriverCore.test.ts b/apps/server/src/vcs/GitVcsDriverCore.test.ts index f93a74f5d3..4784e4fb12 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.test.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.test.ts @@ -2134,6 +2134,59 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { assert.equal(result.branch, current); }), ); + + it.effect("rejects a missing branch without restoring a matching dirty file", () => + Effect.gen(function* () { + const cwd = yield* makeTmpDir(); + const { initialBranch } = yield* initRepoWithCommit(cwd); + const driver = yield* GitVcsDriver.GitVcsDriver; + yield* writeTextFile(cwd, "obsolete-branch", "original\n"); + yield* git(cwd, ["add", "obsolete-branch"]); + yield* git(cwd, ["commit", "-m", "tracked file"]); + yield* git(cwd, ["branch", "obsolete-branch"]); + yield* git(cwd, ["branch", "-D", "obsolete-branch"]); + yield* writeTextFile(cwd, "obsolete-branch", "uncommitted work\n"); + + const result = yield* driver + .switchRef({ cwd, refName: "obsolete-branch" }) + .pipe(Effect.result); + + assert.equal(result._tag, "Failure"); + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + assert.equal( + yield* fileSystem.readFileString(path.join(cwd, "obsolete-branch")), + "uncommitted work\n", + ); + assert.equal(yield* git(cwd, ["branch", "--show-current"]), initialBranch); + }), + ); + + it.effect("still creates and reuses remote tracking branches and allows detached refs", () => + Effect.gen(function* () { + const cwd = yield* makeTmpDir(); + const remote = yield* makeTmpDir("git-remote-"); + const { initialBranch } = yield* initRepoWithCommit(cwd); + const driver = yield* GitVcsDriver.GitVcsDriver; + yield* git(remote, ["init", "--bare"]); + yield* git(cwd, ["remote", "add", "origin", remote]); + yield* git(cwd, ["push", "origin", "HEAD:refs/heads/remote-only"]); + + for (let attempt = 0; attempt < 2; attempt += 1) { + const result = yield* driver.switchRef({ cwd, refName: "origin/remote-only" }); + assert.equal(result.refName, "remote-only"); + assert.equal( + yield* git(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"]), + "origin/remote-only", + ); + yield* driver.switchRef({ cwd, refName: initialBranch }); + } + const commit = yield* git(cwd, ["rev-parse", "HEAD"]); + const detached = yield* driver.switchRef({ cwd, refName: commit }); + assert.equal(detached.refName, null); + assert.equal(yield* git(cwd, ["rev-parse", "HEAD"]), commit); + }), + ); }); describe("worktree operations", () => { diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index 58191a9129..1a872fc338 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -3504,7 +3504,8 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* ? ["checkout", localTrackingBranch] : ["checkout", input.refName]; - yield* executeGit("GitVcsDriver.switchRef.checkout", input.cwd, checkoutArgs, { + // A stale ref must not turn into a path checkout that discards local edits. + yield* executeGit("GitVcsDriver.switchRef.checkout", input.cwd, [...checkoutArgs, "--"], { timeoutMs: 10_000, fallbackErrorDetail: "git checkout failed", }); From 7cd8bb3d5a90ad4af9fce95fa2022d97928cce65 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:34:17 -0600 Subject: [PATCH 021/104] fix(shared): preserve final quoted empty CSV records (#761) --- packages/shared/src/delimitedPreview.test.ts | 29 ++++++++++++++++++++ packages/shared/src/delimitedPreview.ts | 6 ++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/packages/shared/src/delimitedPreview.test.ts b/packages/shared/src/delimitedPreview.test.ts index 303e904285..0188f9da50 100644 --- a/packages/shared/src/delimitedPreview.test.ts +++ b/packages/shared/src/delimitedPreview.test.ts @@ -21,6 +21,35 @@ describe("delimited file previews", () => { ["", "three"], ]); }); + describe.each([",", "\t"] as const)("with delimiter %j", (delimiter) => { + it("preserves a final quoted empty record without requiring a line ending", () => { + for (const ending of ["", "\n", "\r\n"]) { + expect(parseDelimitedPreview(`name\r\nAlice\r\n""${ending}`, delimiter)).toEqual({ + rows: [["name"], ["Alice"], [""]], + truncated: false, + }); + } + expect(parseDelimitedPreview('""', delimiter)).toEqual({ + rows: [[""]], + truncated: false, + }); + }); + it("does not invent records for empty input or a trailing line ending", () => { + for (const prefix of ["", "\ufeff"]) { + expect(parseDelimitedPreview(prefix, delimiter).rows).toEqual([]); + expect(parseDelimitedPreview(`${prefix}name\r\n`, delimiter).rows).toEqual([["name"]]); + expect(parseDelimitedPreview(`${prefix}""`, delimiter).rows).toEqual([[""]]); + } + }); + it("keeps a quoted empty record at the row limit", () => { + const text = `${"name\n".repeat(99)}""`; + const preview = parseDelimitedPreview(text, delimiter); + expect(preview.rows).toHaveLength(100); + expect(preview.rows.at(-1)).toEqual([""]); + expect(preview.truncated).toBe(false); + expect(parseDelimitedPreview(`${text}\nextra`, delimiter).truncated).toBe(true); + }); + }); it("bounds rows, columns and cell length and reports partial content", () => { const table = parseDelimitedPreview( Array.from({ length: 101 }, () => diff --git a/packages/shared/src/delimitedPreview.ts b/packages/shared/src/delimitedPreview.ts index d4e24409a0..1cfbd41a78 100644 --- a/packages/shared/src/delimitedPreview.ts +++ b/packages/shared/src/delimitedPreview.ts @@ -16,12 +16,13 @@ export function parseDelimitedPreview(text: string, delimiter: "," | "\t") { let cell = ""; let quoted = false; let truncated = false; + let rowStart = text.charCodeAt(0) === 0xfeff ? 1 : 0; const endCell = () => { if (row.length < 30) row.push(cell); else truncated = true; cell = ""; }; - for (let index = text.charCodeAt(0) === 0xfeff ? 1 : 0; index < text.length; index++) { + for (let index = rowStart; index < text.length; index++) { const char = text[index]; if (char === '"') { if (quoted && text[index + 1] === '"') { @@ -41,12 +42,13 @@ export function parseDelimitedPreview(text: string, delimiter: "," | "\t") { rows.push(row); row = []; if (char === "\r" && text[index + 1] === "\n") index++; + rowStart = index + 1; if (rows.length === 100) return { rows, truncated: truncated || index < text.length - 1 }; } } else if (cell.length < 2000) cell += char; else truncated = true; } - if (cell.length || row.length) { + if (rowStart < text.length) { endCell(); rows.push(row); } From b7f917958bd6abbc0242b08e2c711a9396cdae01 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:43:49 -0600 Subject: [PATCH 022/104] fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 --- .agents/upstream-review.md | 1 + apps/server/src/git/GitManager.test.ts | 175 +++++++-------- apps/server/src/git/GitManager.ts | 46 +++- .../orchestration/PullRequestSyncReactor.ts | 4 +- .../ThreadSettlementReactor.test.ts | 7 +- .../orchestration/ThreadSettlementReactor.ts | 43 +++- .../pullRequest/GitHubPullRequestCli.test.ts | 190 ++++++++++++---- .../src/pullRequest/GitHubPullRequestCli.ts | 210 +++++++++++++----- .../pullRequest/gitHubPullRequestJson.test.ts | 53 +++++ .../src/pullRequest/gitHubPullRequestJson.ts | 133 +++++++++++ 10 files changed, 652 insertions(+), 210 deletions(-) diff --git a/.agents/upstream-review.md b/.agents/upstream-review.md index a9411b334f..058a7b7dca 100644 --- a/.agents/upstream-review.md +++ b/.agents/upstream-review.md @@ -36,6 +36,7 @@ Historical groups are indexed in the linked archive. This file migration changes | Group / bounded head | Sources | Outcome and remaining scope | Pylon PR / verification | | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Explicit provider refresh cache bypass / `f193a6863c494fdfa36a1ef3132e42a7d7b42926` | `f193a6863c494fdfa36a1ef3132e42a7d7b42926` (#13109) | Implemented in PR #743, pending merge: explicit refresh bypasses the model manifest freshness/retry timer and provider-owned capability, model and package-version caches before status probes. Background probes retain their timers; Pylon's installed-provider `refreshModels` path remains. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #743](https://github.com/pylon-code/pylon/pull/743); independent adversarial review, 246 focused tests, server typecheck and scoped lint/format. | +| Background GitHub PR quota / `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` | `5975ec78b7eaff654e6183b99f8de7a05591e99d` (#13189), `18de6bb328059703b5d1b0661610076e3c0fa2d4` (#13198), `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` (#13200) | Adapted all three: skip uncached recheck when a terminal link cannot settle, batch background PR summaries, and avoid owner-qualified head probes. Preserve Pylon credential scopes, GraphQL budget and per-environment routing; exclude upstream-only test fixture APIs. No cursor advance. | [GitHub quota #749](https://github.com/pylon-code/pylon/pull/749); focused server regressions, scoped lint and typecheck. | | OTLP log export, desktop main process telemetry, and per-signal observability / `fa1e1715538f6f8ac076761f7821f68479f616ab` | `3bb06ad910389fcfc886bd5156f41dc73f307f80` (#12493), `82059df1523d070b00ab6f45159115f626a4977e` (#12520), `a8693eb4ed3a66d72e6b814117e7beeaf0fb5f8b` (#12540), `fa1e1715538f6f8ac076761f7821f68479f616ab` (#12657) | Adopted all four in Cycle #689 (PR #690). Server exports structured log records over OTLP with trace/span ID correlation, replaces `tracerLogger` to eliminate duplicate in-span span-event exports, and applies dedicated flusher/serialization layers. Desktop exports main process traces and logs over OTLP while withholding metric emissions until metrics are instrumented. OTLP settings and HTTP header sets are isolated per signal (traces, metrics, logs) across server and desktop configurations. Adversarial review findings resolved: preserved `pylon-server` runtime and service identity defaults, verified `DEFAULT_SIGNAL_EXPORT` fallback, tested backward compatibility of `ServerObservability` schema decoding, and validated log deduplication. Cursor unchanged. | Cycle [#689](https://github.com/pylon-code/pylon/issues/689), [PR #690](https://github.com/pylon-code/pylon/pull/690); independent adversarial review, new ServerLogger test suite, 9 desktop observability tests, all CI checks green. | | Web chat/diff polish, editor discovery, diff cache eviction, ACP elicitation, and preview recovery / `c14f6015bfe479d313355cb234af1a5c16dbb15f` | `c14f6015bfe479d313355cb234af1a5c16dbb15f` (#12453), `0ff87f251dafb32703d5f531e7b248ad0a581ee2` (#10831), `4a560b4e4ebb37efb7f57805ba79e37f5500bdca` (#12438), `9accc567670da5096e9a694c1adb0d35adb51d9d` (#12571), `dfbb11bdd7c3f1a5575cb55d3e3abb12be025727` (#12377), `408ff8ae9bd7eb2e7e90cbfd8b3fcfe63641bf23` (#12439), `cb3d95c17487f3d834a5537f7c3aa0106bc9b5ec` (#12523), `de6a230db0bb71d3fa91d8de0ae7067b68253f82` (#11294), `efb96939fbb135b9f4de3bff19447200b178b23a` (#7861), `5378f87f99175bded2b9241876319567875aca03` (#12535) | Adopted all ten across Cycle #685 Group A (PR #686) and Group B (PR #687). Retained whitespace when copying diff contents, kept linebreaks in citation content, opened chat popovers with relative placement, discovered installed external editors on desktop outside PATH, wrapped long titles in confirmation dialogs, showed plain text in collapsed thought previews, released oversized pull request diff cache entries, accepted ACP SDK elicitation requests (`session/elicitation` & `elicitation/create`), surfaced Codex app permission requests as approvable, and recovered preview host registration after request timeouts via connection queue eviction. Hardening and adversarial review findings resolved: normalized `acceptAlways` approval decision in Codex session runtime to grant permissions, enforced retirement epoch and quarantine boundaries on approval requests, scoped event emission with `CodexNotificationEpoch`, wrapped diff cache invalidation in `Effect.uninterruptible`, cleaned up mobile icon maps, and rebranded pre-existing PR tool descriptions to Pylon. Preserved Pylon Effect services, session lifecycles, and UI layout authority. Cursor unchanged. | Cycle [#685](https://github.com/pylon-code/pylon/issues/685), [PR #686](https://github.com/pylon-code/pylon/pull/686) and [PR #687](https://github.com/pylon-code/pylon/pull/687); independent adversarial reviews, 260+ focused unit tests across web, server, and effect-acp, full workspace typecheck, scoped format/lint. | | Web interaction, ACP session startup stderr, Claude continuation, and GTK4 snapshot / `52d08a14b9e475371658fabbb12c056c3d3dad67` | `8dd02470b1e7603b8d36a21ae27c510480351f18` (#12552), `7445aa733ada33e45289e5aa5055f79142556513` (#11263), `599c9776eb887d4fff42da7d2daf8aa07ee9a9e2` (#11268), `dcf8942304cfef382af55c0068cb2f04c86757fc` (#12636), `e36725682bd2935f1bcfdf81660cb569b5f3df29` (#12577), `63ff33756c47f127391962458d8388a647e8ed44` (#12624), `d6f291303ddc0c9a14f570266a4d9eff6d431593` (#12625), `52d08a14b9e475371658fabbb12c056c3d3dad67` (#12635) | Adopted all eight across Cycle #682 Group A (PR #683) and Group B (PR #684). Preserved draft question input when clicking options, desktop CSP screenshot annotations, restored providers settings heading, aligned PR detail menu glyphs. Empty Claude homePath correctly resolves and shares continuation with `~/.claude` or `CLAUDE_CONFIG_DIR`, surfaced ACP stderr excerpt in session startup failures instead of generic closed session errors, and extracted desktop accessible text for Flatpak and GTK4 apps in SnapShot. Adversarial review findings resolved: ACP stderr excerpt sanitized against tokens/credentials and pending buffer bounded; defensive empty `homePath` trimming and `CLAUDE_CONFIG_DIR` resolution in OAuth usage; desktop accessibility timeout preserved without premature concurrency overlap. Preserved Pylon provider rate limits and error reporting. Cursor unchanged. | Cycle [#682](https://github.com/pylon-code/pylon/issues/682), [PR #683](https://github.com/pylon-code/pylon/pull/683) and [PR #684](https://github.com/pylon-code/pylon/pull/684); independent adversarial reviews, 225+ focused tests across desktop and server, full workspace typecheck, scoped format/lint. | diff --git a/apps/server/src/git/GitManager.test.ts b/apps/server/src/git/GitManager.test.ts index d4d22be1d2..e6a637bf78 100644 --- a/apps/server/src/git/GitManager.test.ts +++ b/apps/server/src/git/GitManager.test.ts @@ -15,6 +15,7 @@ import * as PlatformError from "effect/PlatformError"; import * as References from "effect/References"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; +import { TestClock } from "effect/testing"; import { ChildProcessSpawner } from "effect/unstable/process"; import { expect } from "vite-plus/test"; import type { @@ -1302,7 +1303,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { prListByHeadSelector: { // Fake gh returns raw JSON stdout, matching the CLI boundary under test. // @effect-diagnostics-next-line preferSchemaOverJson:off - "contributor:feature/deleted-fork-branch": JSON.stringify([ + "feature/deleted-fork-branch": JSON.stringify([ { number: 218, title: "Deleted fork branch PR", @@ -1332,8 +1333,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { updatedAt: "2026-04-05T15:00:00.000Z", }); expect( - ghCalls.some((call) => call.includes("--head contributor:feature/deleted-fork-branch")), + ghCalls.some((call) => call.includes("--head feature/deleted-fork-branch --state all")), ).toBe(true); + expect(ghCalls.some((call) => call.includes("--head contributor:"))).toBe(false); }), ); @@ -1484,6 +1486,61 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }), ); + it.effect("branch PR lookup rechecks open PRs every minute and settled answers less often", () => + Effect.gen(function* () { + const repoDir = yield* makeTempDir("t3code-git-manager-"); + yield* initRepo(repoDir); + const remoteDir = yield* createBareRemote(); + yield* runGit(repoDir, ["remote", "add", "origin", remoteDir]); + for (const branch of ["feature/open-pr", "feature/merged-pr", "feature/no-pr"]) { + yield* runGit(repoDir, ["checkout", "-b", branch, "main"]); + yield* runGit(repoDir, ["push", "-u", "origin", branch]); + } + const pullRequest = (number: number, headRefName: string, state: string) => ({ + number, + title: headRefName, + url: `https://github.com/pingdotgg/codething-mvp/pull/${number}`, + baseRefName: "main", + headRefName, + state, + updatedAt: "2026-04-07T15:00:00Z", + }); + const { manager, ghCalls } = yield* makeManager({ + ghScenario: { + prListByHeadSelector: { + "feature/open-pr": encodeCliJson([pullRequest(301, "feature/open-pr", "OPEN")]), + "feature/merged-pr": encodeCliJson([pullRequest(302, "feature/merged-pr", "MERGED")]), + }, + }, + }); + const lookupAll = Effect.forEach( + ["feature/open-pr", "feature/merged-pr", "feature/no-pr"], + (branch) => manager.branchPullRequest({ cwd: repoDir, branch }), + ); + const prListCalls = () => ghCalls.filter((call) => call.startsWith("pr list ")); + + yield* lookupAll; + expect(prListCalls()).toHaveLength(3); + + yield* TestClock.adjust("61 seconds"); + yield* lookupAll; + expect(prListCalls()).toHaveLength(4); + expect(prListCalls().at(-1)).toContain("--head feature/open-pr"); + + // Just inside the 5-minute window only the open PR is asked again. + yield* TestClock.adjust("238 seconds"); + yield* lookupAll; + expect(prListCalls()).toHaveLength(5); + expect(prListCalls().at(-1)).toContain("--head feature/open-pr"); + + // Just past it the settled answers expire too. + yield* TestClock.adjust("2 seconds"); + yield* lookupAll; + expect(prListCalls()).toHaveLength(7); + expect(prListCalls().slice(-2).join("\n")).not.toContain("--head feature/open-pr"); + }), + ); + it.effect("branch PR lookup propagates provider failures", () => Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); @@ -1780,8 +1837,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const { manager } = yield* makeManager({ ghScenario: { prListByHeadSelector: { - "alice:feature": output, - "fork:feature": output, feature: output, }, }, @@ -1948,10 +2003,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const { manager, ghCalls } = yield* makeManager({ ghScenario: { prListSequence: [ - // @effect-diagnostics-next-line preferSchemaOverJson:off - JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - JSON.stringify([]), // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify([ { @@ -1987,7 +2038,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { updatedAt: "2026-03-10T07:00:00.000Z", }); expect(ghCalls).toContain( - "pr list --head jasonLaster:statemachine --state all --limit 20 --json number,title,url,baseRefName,headRefName,state,isDraft,mergedAt,closedAt,updatedAt,isCrossRepository,headRepository,headRepositoryOwner", + "pr list --head statemachine --state all --limit 100 --json number,title,url,baseRefName,headRefName,state,isDraft,mergedAt,closedAt,updatedAt,isCrossRepository,headRepository,headRepositoryOwner", ); }), 20_000, @@ -2019,7 +2070,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { ghScenario: { prListByHeadSelector: { // @effect-diagnostics-next-line preferSchemaOverJson:off - "contributor:main": JSON.stringify([ + main: JSON.stringify([ { number: 777, title: "Fork PR from main", @@ -2053,7 +2104,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { updatedAt: "2026-03-10T07:00:00.000Z", }); expect(ghCalls).toContain( - "pr list --head contributor:main --state all --limit 20 --json number,title,url,baseRefName,headRefName,state,isDraft,mergedAt,closedAt,updatedAt,isCrossRepository,headRepository,headRepositoryOwner", + "pr list --head main --state all --limit 100 --json number,title,url,baseRefName,headRefName,state,isDraft,mergedAt,closedAt,updatedAt,isCrossRepository,headRepository,headRepositoryOwner", ); }), 20_000, @@ -2118,34 +2169,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { updatedAt: "2026-04-01T10:00:00Z", }, ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "pingdotgg:effect-atom": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "my-org/upstream:effect-atom": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "pingdotgg:upstream/effect-atom": JSON.stringify([ - { - number: 1518, - title: "Wrong PR", - url: "https://github.com/pingdotgg/t3code/pull/1518", - baseRefName: "main", - headRefName: "upstream/effect-atom", - state: "OPEN", - updatedAt: "2026-04-01T10:00:00Z", - }, - ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "my-org/upstream:upstream/effect-atom": JSON.stringify([ - { - number: 1518, - title: "Wrong PR", - url: "https://github.com/pingdotgg/t3code/pull/1518", - baseRefName: "main", - headRefName: "upstream/effect-atom", - state: "OPEN", - updatedAt: "2026-04-01T10:00:00Z", - }, - ]), }, }, }); @@ -2356,7 +2379,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { prListByHeadSelector: { // Fake gh returns raw JSON stdout, matching the CLI boundary under test. // @effect-diagnostics-next-line preferSchemaOverJson:off - "contributor:feature/fork-plain": JSON.stringify([ + "feature/fork-plain": JSON.stringify([ { number: 89, title: "Fork PR pushed without -u", @@ -2376,9 +2399,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const status = yield* manager.status({ cwd: repoDir }); expect(status.pr?.number).toBe(89); - expect(ghCalls.some((call) => call.includes("--head contributor:feature/fork-plain"))).toBe( - true, - ); + expect(ghCalls.some((call) => call.includes("--head feature/fork-plain"))).toBe(true); + expect(ghCalls.some((call) => call.includes("--head contributor:"))).toBe(false); expect(ghCalls.some((call) => call.includes("--head main"))).toBe(false); }), ); @@ -2413,7 +2435,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { prListByHeadSelector: { // Fake gh returns raw JSON stdout, matching the CLI boundary under test. // @effect-diagnostics-next-line preferSchemaOverJson:off - "contributor:feature/fork-settle": JSON.stringify([ + "feature/fork-settle": JSON.stringify([ { number: 91, title: "Fork PR to settle", @@ -3697,7 +3719,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { ); it.effect( - "returns existing cross-repo PR metadata using the fork owner selector", + "returns existing cross-repo PR metadata found under the bare branch name", () => Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); @@ -3716,8 +3738,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const { manager, ghCalls } = yield* makeManager({ ghScenario: { prListSequence: [ - // @effect-diagnostics-next-line preferSchemaOverJson:off - JSON.stringify([]), // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify([ { @@ -3749,7 +3769,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.pr.number).toBe(142); expect( ghCalls.some((call) => - call.includes("pr list --head octocat:statemachine --state open --limit 1"), + call.includes("pr list --head statemachine --state open --limit 100"), ), ).toBe(true); expect(ghCalls.some((call) => call.startsWith("pr create "))).toBe(false); @@ -3815,30 +3835,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { headRefName: "upstream/effect-atom", }, ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "pingdotgg:effect-atom": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "my-org/upstream:effect-atom": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "pingdotgg:upstream/effect-atom": JSON.stringify([ - { - number: 1518, - title: "Wrong PR", - url: "https://github.com/pingdotgg/t3code/pull/1518", - baseRefName: "main", - headRefName: "upstream/effect-atom", - }, - ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "my-org/upstream:upstream/effect-atom": JSON.stringify([ - { - number: 1518, - title: "Wrong PR", - url: "https://github.com/pingdotgg/t3code/pull/1518", - baseRefName: "main", - headRefName: "upstream/effect-atom", - }, - ]), }, }, }); @@ -3858,7 +3854,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { ); it.effect( - "prefers owner-qualified selectors before bare branch names for cross-repo PRs", + "picks the fork PR among same-named branches from other repositories", () => Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); @@ -3890,9 +3886,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { baseRefName: "main", headRefName: "statemachine", }, - ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "octocat:statemachine": JSON.stringify([ { number: 142, title: "Existing fork PR", @@ -3909,8 +3902,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }, }, ]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - "fork-seed:statemachine": JSON.stringify([]), }, }, }); @@ -3922,11 +3913,9 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.pr.status).toBe("opened_existing"); expect(result.pr.number).toBe(142); - - const ownerSelectorCallIndex = ghCalls.findIndex((call) => - call.includes("pr list --head octocat:statemachine --state open --limit 1"), - ); - expect(ownerSelectorCallIndex).toBeGreaterThanOrEqual(0); + expect( + ghCalls.some((call) => /--head [^ ]*:/u.test(call) && call.startsWith("pr list")), + ).toBe(false); expect(ghCalls.some((call) => call.startsWith("pr create "))).toBe(false); }), 12_000, @@ -3955,7 +3944,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { ghScenario: { prListByHeadSelector: { // @effect-diagnostics-next-line preferSchemaOverJson:off - "octocat:statemachine": JSON.stringify([ + statemachine: JSON.stringify([ { number: 142, title: "Existing fork PR", @@ -3973,11 +3962,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }, ]), // @effect-diagnostics-next-line preferSchemaOverJson:off - "fork-seed:statemachine": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off "t3code/pr-142/statemachine": JSON.stringify([]), - // @effect-diagnostics-next-line preferSchemaOverJson:off - statemachine: JSON.stringify([]), }, }, }); @@ -3990,10 +3975,10 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.pr.status).toBe("opened_existing"); expect(result.pr.number).toBe(142); - const openLookupCalls = ghCalls.filter((call) => call.includes("--state open --limit 1")); + const openLookupCalls = ghCalls.filter((call) => call.includes("--state open --limit 100")); expect(openLookupCalls).toHaveLength(1); expect(openLookupCalls[0]).toContain( - "pr list --head octocat:statemachine --state open --limit 1", + "pr list --head statemachine --state open --limit 100", ); }), 12_000, @@ -4018,12 +4003,10 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const { manager, ghCalls } = yield* makeManager({ ghScenario: { prListSequenceByHeadSelector: { - "octocat:statemachine": [ + statemachine: [ `[{"number":41,"title":"Ambiguous fork PR","url":"https://github.com/pingdotgg/codething-mvp/pull/41","baseRefName":"main","headRefName":"statemachine","state":"OPEN"}]`, `[{"number":142,"title":"Add stacked git actions","url":"https://github.com/pingdotgg/codething-mvp/pull/142","baseRefName":"main","headRefName":"statemachine","state":"OPEN","isCrossRepository":true,"headRepository":{"nameWithOwner":"octocat/codething-mvp"},"headRepositoryOwner":{"login":"octocat"}}]`, ], - "fork-seed:statemachine": ["[]"], - statemachine: ["[]"], }, }, }); @@ -4344,7 +4327,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { const { manager, ghCalls } = yield* makeManager({ ghScenario: { prListSequenceByHeadSelector: { - "octocat:statemachine": [ + statemachine: [ // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify([]), // @effect-diagnostics-next-line preferSchemaOverJson:off @@ -4366,10 +4349,6 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }, ]), ], - // @effect-diagnostics-next-line preferSchemaOverJson:off - "fork-seed:statemachine": [JSON.stringify([])], - // @effect-diagnostics-next-line preferSchemaOverJson:off - statemachine: [JSON.stringify([])], }, }, }); diff --git a/apps/server/src/git/GitManager.ts b/apps/server/src/git/GitManager.ts index 8bec757d12..42e63304e0 100644 --- a/apps/server/src/git/GitManager.ts +++ b/apps/server/src/git/GitManager.ts @@ -31,6 +31,7 @@ import { ModelSelection, type ProjectId, SourceControlProviderError, + type SourceControlProviderKind, type SourceControlWritingStyleSettings, type ThreadId, } from "@t3tools/contracts"; @@ -143,6 +144,12 @@ const STATUS_RESULT_CACHE_CAPACITY = 2_048; // exponentially via prLookupFailureTtl, so throttling pressure still drops // under 429s instead of amplifying it. const PR_LOOKUP_CACHE_TTL = Duration.seconds(60); +// Answers without an open PR ("no PR yet", merged, closed) only change when +// someone opens a PR, and the paths that do that in-app (turn end, push, +// create PR, user refresh) bypass this cache. Re-asking every minute for each +// idle branch was the bulk of background GitHub quota use, so these wait out +// a longer TTL and a PR opened outside the app shows up within minutes. +const PR_LOOKUP_NO_OPEN_PR_CACHE_TTL = Duration.minutes(5); const PR_LOOKUP_FAILURE_BASE_TTL = Duration.seconds(20); const PR_LOOKUP_FAILURE_MAX_TTL = Duration.minutes(15); const PR_LOOKUP_CACHE_CAPACITY = 2_048; @@ -578,6 +585,26 @@ function parseCustomCommitMessage(raw: string): { subject: string; body: string }; } +// Without the owner selector, a bare branch name also lists same-named +// branches on other forks (`main`, `patch-1`), so GitHub probes ask for a full +// page and let matchesBranchHeadContext pick the right head. gh fetches up to +// 100 in one request, and GitHub prices a first:100 connection like first:1. +const GITHUB_HEAD_BRANCH_PROBE_LIMIT = 100; + +// `gh pr list --head` filters on the head ref name alone and accepts anything, so an +// `owner:branch` or `remote:branch` selector silently lists zero pull requests +// while spending a GraphQL call. Git branch names cannot contain ":", and the +// bare head branch is always among the selectors, so GitHub probes skip them and +// leave the owner check to matchesBranchHeadContext. +function probeableHeadSelectors( + providerKind: SourceControlProviderKind, + headSelectors: ReadonlyArray, +): ReadonlyArray { + return providerKind === "github" + ? headSelectors.filter((selector) => !selector.includes(":")) + : headSelectors; +} + function appendUnique(values: string[], next: string | null | undefined): void { const trimmed = next?.trim() ?? ""; if (trimmed.length === 0 || values.includes(trimmed)) { @@ -1126,7 +1153,9 @@ export const make = Effect.gen(function* () { timeToLive: (exit, key) => { if (Exit.isSuccess(exit)) { prLookupFailureStreakByKey.delete(key); - return PR_LOOKUP_CACHE_TTL; + return exit.value.latest?.state === "open" + ? PR_LOOKUP_CACHE_TTL + : PR_LOOKUP_NO_OPEN_PR_CACHE_TTL; } return nextPrLookupFailureTtl(key); }, @@ -1610,12 +1639,14 @@ export const make = Effect.gen(function* () { | "isCrossRepository" >, ) { - for (const headSelector of headContext.headSelectors) { - const pullRequests = yield* (yield* sourceControlProvider(cwd)).listChangeRequests({ + const provider = yield* sourceControlProvider(cwd); + const headSelectors = probeableHeadSelectors(provider.kind, headContext.headSelectors); + for (const headSelector of headSelectors) { + const pullRequests = yield* provider.listChangeRequests({ cwd, headSelector, state: "open", - limit: 1, + limit: provider.kind === "github" ? GITHUB_HEAD_BRANCH_PROBE_LIMIT : 1, }); const normalizedPullRequests = pullRequests.map(toPullRequestInfo); @@ -1640,12 +1671,13 @@ export const make = Effect.gen(function* () { ) { const parsedByNumber = new Map(); - for (const headSelector of headContext.headSelectors) { - const pullRequests = yield* (yield* sourceControlProvider(cwd)).listChangeRequests({ + const provider = yield* sourceControlProvider(cwd); + for (const headSelector of probeableHeadSelectors(provider.kind, headContext.headSelectors)) { + const pullRequests = yield* provider.listChangeRequests({ cwd, headSelector, state: "all", - limit: 20, + limit: provider.kind === "github" ? GITHUB_HEAD_BRANCH_PROBE_LIMIT : 20, }); for (const pr of pullRequests.map(toPullRequestInfo)) { diff --git a/apps/server/src/orchestration/PullRequestSyncReactor.ts b/apps/server/src/orchestration/PullRequestSyncReactor.ts index 0a39fa5d91..eb74dc3486 100644 --- a/apps/server/src/orchestration/PullRequestSyncReactor.ts +++ b/apps/server/src/orchestration/PullRequestSyncReactor.ts @@ -297,7 +297,9 @@ export const make = Effect.gen(function* () { Effect.catchCause(logSkipped("pull request sync skipped", { key })), ) : Effect.void, - { concurrency: 8, discard: true }, + // As wide as one batched summary read, so the sweep's reads on a host arrive together and + // GitHub answers them in one request rather than one `gh pr view` apiece. + { concurrency: 25, discard: true }, ); }); diff --git a/apps/server/src/orchestration/ThreadSettlementReactor.test.ts b/apps/server/src/orchestration/ThreadSettlementReactor.test.ts index 455cb37a61..03519e0fe3 100644 --- a/apps/server/src/orchestration/ThreadSettlementReactor.test.ts +++ b/apps/server/src/orchestration/ThreadSettlementReactor.test.ts @@ -417,12 +417,12 @@ describe("ThreadSettlementReactor", () => { makeThread("reused-detected", { branch: "reused", branchPullRequest: previous }), makeThread("foreign-branch-pr", { branch: "foreign", linkedPullRequest: previous }), makeThread("resumed-manual", { - branch: "main", + branch: "resumed", linkedPullRequest: previous, latestUserMessageAt: "2026-08-28T00:00:00.000Z", }), makeThread("resumed-detected", { - branch: "main", + branch: "resumed", branchPullRequest: previous, latestUserMessageAt: "2026-08-28T00:00:00.000Z", }), @@ -458,6 +458,9 @@ describe("ThreadSettlementReactor", () => { new Set((yield* Ref.get(fixture.commands)).map((command) => command.threadId)), new Set([ThreadId.make("retained-terminal"), ThreadId.make("foreign-branch-pr")]), ); + assert.isFalse( + (yield* Ref.get(fixture.branchCalls)).some(({ branch }) => branch === "resumed"), + ); }).pipe(Effect.provide(fixture.layer)); }), ), diff --git a/apps/server/src/orchestration/ThreadSettlementReactor.ts b/apps/server/src/orchestration/ThreadSettlementReactor.ts index b9041d2976..193759d9c4 100644 --- a/apps/server/src/orchestration/ThreadSettlementReactor.ts +++ b/apps/server/src/orchestration/ThreadSettlementReactor.ts @@ -199,9 +199,30 @@ export const make = Effect.gen(function* () { }; const groups = Map.groupBy(lookupCandidates, lookupKey); + const wouldSettle = Effect.fn("ThreadSettlementReactor.wouldSettle")(function* ( + group: ReadonlyArray<(typeof candidates)[number]>, + pullRequest: SettlementPullRequest, + ) { + const currentSettings = yield* settingsService.getSettings; + const decisionNow = DateTime.formatIso(yield* DateTime.now); + return group.some((thread) => { + const { settings } = resolveProjectSettings(currentSettings, thread.projectId); + return ( + resolveAutoSettlementAt({ + thread, + pullRequest, + now: decisionNow, + autoSettleAfterDays: settings.sidebarAutoSettleAfterDays, + autoSettleOnMerge: settings.sidebarAutoSettleOnMerge, + }) !== null + ); + }); + }); + const pullRequestFor = Effect.fn("ThreadSettlementReactor.pullRequestFor")(function* ( - thread: (typeof candidates)[number], + group: ReadonlyArray<(typeof candidates)[number]>, ) { + const thread = group[0]!; const reference = thread.linkedPullRequest ?? thread.branchPullRequest; if (reference != null) { const matchesMerge = @@ -226,10 +247,21 @@ export const make = Effect.gen(function* () { }, { recoverTransientFailure: false }, ); + const terminal = { + state: summary.state, + closedAt: summary.closedAt ?? null, + mergedAt: summary.mergedAt ?? null, + } satisfies SettlementPullRequest; const cwd = lookupCwdByThreadId.get(thread.id); if (summary.state !== "open" && thread.branch !== null && cwd !== undefined) { // A reused branch can already have a new open PR while discovery // is replacing its old link. Do not let settlement win that race. + // Only pay for the uncached lookup when this sweep would otherwise + // settle: a terminal link that settles nothing (resumed thread, + // settle-on-merge off) would re-query the host every minute. A + // group that becomes eligible after this check waits for the next + // sweep rather than settling on the unverified link. + if (!(yield* wouldSettle(group, terminal))) return undefined; const current = yield* git.branchPullRequest( { cwd, branch: thread.branch }, { refresh: true }, @@ -243,11 +275,7 @@ export const make = Effect.gen(function* () { return current; } } - return { - state: summary.state, - closedAt: summary.closedAt ?? null, - mergedAt: summary.mergedAt ?? null, - } satisfies SettlementPullRequest; + return terminal; } if (thread.branch === null) return null; const cwd = lookupCwdByThreadId.get(thread.id); @@ -261,7 +289,8 @@ export const make = Effect.gen(function* () { groups.values(), (group) => Effect.gen(function* () { - const pullRequest = yield* pullRequestFor(group[0]!); + const pullRequest = yield* pullRequestFor(group); + if (pullRequest === undefined) return; yield* Effect.forEach(group, (thread) => settleThread(thread, pullRequest), { discard: true, }); diff --git a/apps/server/src/pullRequest/GitHubPullRequestCli.test.ts b/apps/server/src/pullRequest/GitHubPullRequestCli.test.ts index 8416083061..ed8710ee53 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestCli.test.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestCli.test.ts @@ -1,5 +1,6 @@ import { afterEach, assert, expect, it, vi } from "@effect/vitest"; import * as Effect from "effect/Effect"; +import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; @@ -193,77 +194,137 @@ afterEach(() => { }); layer("GitHubPullRequestCli.layer", (it) => { - it.effect("reads linked pull request status with the overview fields in one request", () => + it.effect("reads linked pull requests on one host together, filed back by position", () => Effect.gen(function* () { + const node = (number: number) => ({ + number, + title: `Pull request ${number}`, + url: `https://github.com/acme/web/pull/${number}`, + author: { __typename: "User", login: "octocat", name: "Octo Cat", avatarUrl: null }, + baseRefName: "main", + headRefName: `feat/${number}`, + state: "OPEN", + isDraft: false, + mergeable: "MERGEABLE", + reviewDecision: null, + latestReviews: { nodes: [{ state: "APPROVED", author: { login: "reviewer" } }] }, + additions: 12, + deletions: 3, + changedFiles: 2, + updatedAt: "2026-08-24T12:34:56.000Z", + mergedAt: null, + closedAt: null, + commits: { nodes: [{ commit: { statusCheckRollup: { state: "SUCCESS" } } }] }, + }); mockedExecute.mockReturnValueOnce( Effect.succeed( output( // @effect-diagnostics-next-line preferSchemaOverJson:off JSON.stringify({ - number: 7, - title: "Reuse the summary", - url: "https://github.com/acme/web/pull/7", - author: { login: "octocat", name: "Octo Cat" }, - baseRefName: "main", - headRefName: "feat/summary", - state: "OPEN", - isDraft: false, - mergeable: "MERGEABLE", - reviewDecision: "APPROVED", - additions: 12, - deletions: 3, - changedFiles: 2, - createdAt: "2026-08-20T00:00:00.000Z", - updatedAt: "2026-08-24T12:34:56.000Z", - reviewRequests: [], - labels: [], - statusCheckRollup: [ - { __typename: "CheckRun", status: "COMPLETED", conclusion: "SUCCESS", name: "ci" }, - ], - body: "", + data: { s0: { pullRequest: node(7) }, s1: { pullRequest: node(8) } }, }), ), ), ); const cli = yield* GitHubPullRequestCli.GitHubPullRequestCli; - const summary = yield* cli.getPullRequestSummary({ - cwd: "/w", - repository: "acme/web", - host: "github.com", - number: 7, - }); + const reads = yield* Effect.forEach( + [7, 8], + (number) => + cli.getPullRequestSummary({ + cwd: "/w", + repository: "acme/web", + host: "github.com", + number, + }), + { concurrency: "unbounded" }, + ).pipe(Effect.forkChild); + yield* TestClock.adjust("10 millis"); + const [seven, eight] = yield* Fiber.join(reads); assert.deepStrictEqual( { - number: summary.number, - state: summary.state, - headBranch: summary.headBranch, - isDraft: summary.isDraft, - author: summary.author?.login, - additions: summary.additions, - deletions: summary.deletions, - changedFiles: summary.changedFiles, - reviewDecision: summary.reviewDecision, - checksState: summary.checksState, - mergeability: summary.mergeability, + number: seven?.number, + state: seven?.state, + headBranch: seven?.headBranch, + author: seven?.author?.login, + changedFiles: seven?.changedFiles, + reviewDecision: seven?.reviewDecision, + checksState: seven?.checksState, + mergeability: seven?.mergeability, }, { number: 7, state: "open", - headBranch: "feat/summary", - isDraft: false, + headBranch: "feat/7", author: "octocat", - additions: 12, - deletions: 3, changedFiles: 2, reviewDecision: "approved", checksState: "passing", mergeability: "mergeable", }, ); + assert.strictEqual(eight?.headBranch, "feat/8"); expect(mockedExecute).toHaveBeenCalledOnce(); - expect(mockedExecute.mock.calls[0]?.[0]?.args).toEqual([ + const document = callAt(0).args.at(-1) ?? ""; + expect(document).toContain( + 's0: repository(owner: "acme", name: "web") { pullRequest(number: 7)', + ); + expect(document).toContain("pullRequest(number: 8)"); + }), + ); + + it.effect("reads a pull request the batch said nothing about on its own", () => + Effect.gen(function* () { + mockedExecute + .mockReturnValueOnce(Effect.succeed(output('{"data":{"s0":{"pullRequest":null}}}'))) + .mockReturnValueOnce( + Effect.succeed( + output( + // @effect-diagnostics-next-line preferSchemaOverJson:off + JSON.stringify({ + number: 7, + title: "Reuse the summary", + url: "https://github.com/acme/web/pull/7", + author: { login: "octocat", name: "Octo Cat" }, + baseRefName: "main", + headRefName: "feat/summary", + state: "OPEN", + isDraft: false, + mergeable: "MERGEABLE", + reviewDecision: "APPROVED", + additions: 12, + deletions: 3, + changedFiles: 2, + createdAt: "2026-08-20T00:00:00.000Z", + updatedAt: "2026-08-24T12:34:56.000Z", + reviewRequests: [], + labels: [], + statusCheckRollup: [ + { + __typename: "CheckRun", + status: "COMPLETED", + conclusion: "SUCCESS", + name: "ci", + }, + ], + body: "", + }), + ), + ), + ); + const cli = yield* GitHubPullRequestCli.GitHubPullRequestCli; + + const read = yield* cli + .getPullRequestSummary({ cwd: "/w", repository: "acme/web", host: "github.com", number: 7 }) + .pipe(Effect.forkChild); + yield* TestClock.adjust("10 millis"); + const summary = yield* Fiber.join(read); + + assert.strictEqual(summary.headBranch, "feat/summary"); + assert.strictEqual(summary.checksState, "passing"); + assert.strictEqual(mockedExecute.mock.calls.length, 2); + expect(callAt(1).args).toEqual([ "pr", "view", "7", @@ -272,7 +333,46 @@ layer("GitHubPullRequestCli.layer", (it) => { "--json", expect.stringContaining("statusCheckRollup"), ]); - expect(mockedGetPullRequest).not.toHaveBeenCalled(); + }), + ); + + it.effect("does not fan out summary reads after a rejected GraphQL batch", () => + Effect.gen(function* () { + for (const response of [ + Effect.succeed(output('{"data":null,"errors":[{"message":"API rate limit exceeded"}]}')), + Effect.fail( + new GitHubCli.GitHubCliRateLimitError({ + command: "gh", + cwd: "/w", + cause: new Error("API rate limit exceeded"), + }), + ), + ]) { + mockedExecute.mockReset().mockReturnValueOnce(response); + const cli = yield* GitHubPullRequestCli.GitHubPullRequestCli; + const reads = yield* Effect.all([ + cli + .getPullRequestSummary({ + cwd: "/w", + repository: "acme/web", + host: "github.com", + number: 7, + }) + .pipe(Effect.forkChild), + cli + .getPullRequestSummary({ + cwd: "/w", + repository: "acme/web", + host: "github.com", + number: 8, + }) + .pipe(Effect.forkChild), + ]); + yield* TestClock.adjust("10 millis"); + const exits = yield* Effect.forEach(reads, (read) => Fiber.await(read)); + expect(exits.every((exit) => exit._tag === "Failure")).toBe(true); + expect(mockedExecute).toHaveBeenCalledOnce(); + } }), ); diff --git a/apps/server/src/pullRequest/GitHubPullRequestCli.ts b/apps/server/src/pullRequest/GitHubPullRequestCli.ts index d90d7378fa..b6c9dae750 100644 --- a/apps/server/src/pullRequest/GitHubPullRequestCli.ts +++ b/apps/server/src/pullRequest/GitHubPullRequestCli.ts @@ -1,7 +1,12 @@ import { runGitHubStackAction, type GitHubStackActionError } from "./githubStackActions.ts"; +import * as Cause from "effect/Cause"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import * as Exit from "effect/Exit"; +import * as Option from "effect/Option"; +import * as Request from "effect/Request"; +import * as RequestResolver from "effect/RequestResolver"; import * as Result from "effect/Result"; import * as Schema from "effect/Schema"; import { @@ -36,6 +41,7 @@ import { decodeActorAvatarsJson, decodePullRequestActivityJson, decodePullRequestDetailJson, + type GitHubPullRequestSummary, decodePullRequestFilesJson, decodePullRequestHeadsJson, decodePullRequestListJson, @@ -43,6 +49,8 @@ import { decodePullRequestSearchJson, decodePullRequestStacksJson, decodePullRequestStatsJson, + decodePullRequestSummariesJson, + GitHubSummaryBatchUnavailableError, decodeReactionSubjectScopeJson, decodeRepositoryAccessJson, decodeReviewerCandidatesJson, @@ -53,6 +61,7 @@ import { decodeReviewThreadCommentsJson, decodeReviewThreadsJson, buildPullRequestStatsGraphQlQuery, + buildPullRequestSummariesGraphQlQuery, buildPullRequestStackMembershipsGraphQlQuery, decodePullRequestStackMembershipsJson, encodeGraphQlRequestJson, @@ -393,6 +402,23 @@ export interface GitHubPullRequestStat { */ const STAT_ALIASES_PER_REQUEST = 25; const STAT_REQUEST_CONCURRENCY = 4; +/** + * How long a summary read waits for company. The background sync asks for every linked pull + * request at once, and each read reaches the resolver after its own cache check, so a batch + * needs a moment longer than one scheduler tick to gather them. + */ +const SUMMARY_BATCH_WINDOW = "10 millis"; + +class PullRequestSummaryRead extends Request.Class< + { + readonly cwd: string; + readonly repository: string; + readonly host: string; + readonly number: number; + }, + ProviderChangeRequestSummary, + GitHubPullRequestCliError +> {} export interface GitHubPullRequestSearchBatch { /** Rows across every repository asked for, newest update first, each naming its own. */ @@ -1467,6 +1493,139 @@ export const make = Effect.gen(function* () { ), ); + // One `gh pr view` either way; asking for the detail fields costs nothing extra and hands + // the thread overview its author, diff stat, review decision and checks in the same read. + const viewPullRequestSummary = (input: PullRequestSummaryRead) => + github + .execute({ + cwd: input.cwd, + args: [ + "pr", + "view", + String(input.number), + ...repositoryArgs(input), + "--json", + PULL_REQUEST_DETAIL_JSON_FIELDS, + ], + }) + .pipe( + Effect.flatMap((result) => { + const decoded = decodePullRequestDetailJson(result.stdout.trim()); + if (!Result.isSuccess(decoded)) { + return Effect.fail( + new GitHubPullRequestReadError({ + command: "gh", + cwd: input.cwd, + operation: "getPullRequestSummary", + cause: decoded.failure, + }), + ); + } + const detail = decoded.success; + return Effect.succeed({ + number: detail.number, + title: detail.title, + url: detail.url, + headBranch: detail.headBranch, + baseBranch: detail.baseBranch, + state: detail.state, + updatedAt: detail.updatedAt, + closedAt: detail.closedAt ?? null, + mergedAt: detail.mergedAt ?? null, + isDraft: detail.isDraft, + author: detail.author, + additions: detail.additions, + deletions: detail.deletions, + changedFiles: detail.changedFiles, + reviewDecision: detail.reviewDecision, + checksState: detail.checksState, + mergeability: detail.mergeability, + }); + }), + ); + + /** + * Summaries asked for together, on one host under one credential, share aliased GraphQL reads + * of twenty-five: the background sync reads every linked pull request each minute, and one + * `gh pr view` apiece is most of what it spends. Whatever the batch cannot answer — a selector + * GraphQL cannot address, a pull request GitHub returned nothing for — is read on its own. + */ + const summaryResolver = RequestResolver.makeGrouped({ + key: ({ request, context }) => + JSON.stringify([ + request.host.toLowerCase(), + Context.getOrElse(context, GitHubCli.PinnedGitHubCredential, () => null) + ?.credentialFingerprint ?? null, + Context.getOrElse(context, SourceControlRateLimit.CredentialScope, () => ""), + ]), + resolver: (entries) => { + const [first] = entries; + const batchable = entries.filter( + (entry) => buildPullRequestSummariesGraphQlQuery([entry.request]) !== null, + ); + const query = buildPullRequestSummariesGraphQlQuery(batchable.map((entry) => entry.request)); + const batched = + query === null + ? Effect.succeed(new Map()) + : graphqlRead({ + cwd: first.request.cwd, + host: first.request.host, + operation: "getPullRequestSummary", + query, + decode: decodePullRequestSummariesJson, + }); + return batched.pipe( + // A GraphQL error anywhere fails the whole document — one repository gone or out of + // reach — so a batch that could not be read leaves every entry to its own read. A paused + // budget is the exception: reading one at a time would only spend what is being saved. + Effect.catchCauseIf( + (cause) => + !Cause.hasInterruptsOnly(cause) && + !Cause.findErrorOption(cause).pipe( + Option.exists( + (error) => + error._tag === "SourceControlRateLimitPausedError" || + error._tag === "GitHubCliRateLimitError" || + (error._tag === "GitHubPullRequestReadError" && + error.cause instanceof GitHubSummaryBatchUnavailableError), + ), + ), + (cause) => + Effect.logDebug("batched pull request summary read failed", { cause }).pipe( + Effect.as(new Map()), + ), + ), + Effect.flatMap((summaries) => { + const unanswered = entries.filter((entry) => { + const summary = summaries.get(batchable.indexOf(entry)); + if (summary === undefined) return true; + entry.completeUnsafe(Exit.succeed(summary)); + return false; + }); + return Effect.forEach( + unanswered, + (entry) => + viewPullRequestSummary(entry.request).pipe( + Effect.exit, + Effect.map((exit) => entry.completeUnsafe(exit)), + ), + { concurrency: STAT_REQUEST_CONCURRENCY, discard: true }, + ); + }), + Effect.catchCause((cause) => + Effect.sync(() => { + for (const entry of entries) entry.completeUnsafe(Exit.failCause(cause)); + }), + ), + ); + }, + }).pipe( + RequestResolver.setDelay(SUMMARY_BATCH_WINDOW), + RequestResolver.batchN(STAT_ALIASES_PER_REQUEST), + ); + const getPullRequestSummary: GitHubPullRequestCli["Service"]["getPullRequestSummary"] = (input) => + Effect.request(new PullRequestSummaryRead(input), summaryResolver); + return GitHubPullRequestCli.of({ getViewerLogin: (input) => github.execute({ cwd: input.cwd, args: ["api", "user", "--jq", ".login"] }).pipe( @@ -1678,56 +1837,7 @@ export const make = Effect.gen(function* () { ).pipe(Effect.map((results) => results.flat())); }, - // One `gh pr view` either way; asking for the detail fields costs nothing extra and hands - // the thread overview its author, diff stat, review decision and checks in the same read. - getPullRequestSummary: (input) => - github - .execute({ - cwd: input.cwd, - args: [ - "pr", - "view", - String(input.number), - ...repositoryArgs(input), - "--json", - PULL_REQUEST_DETAIL_JSON_FIELDS, - ], - }) - .pipe( - Effect.flatMap((result) => { - const decoded = decodePullRequestDetailJson(result.stdout.trim()); - if (!Result.isSuccess(decoded)) { - return Effect.fail( - new GitHubPullRequestReadError({ - command: "gh", - cwd: input.cwd, - operation: "getPullRequestSummary", - cause: decoded.failure, - }), - ); - } - const detail = decoded.success; - return Effect.succeed({ - number: detail.number, - title: detail.title, - url: detail.url, - headBranch: detail.headBranch, - baseBranch: detail.baseBranch, - state: detail.state, - updatedAt: detail.updatedAt, - closedAt: detail.closedAt ?? null, - mergedAt: detail.mergedAt ?? null, - isDraft: detail.isDraft, - author: detail.author, - additions: detail.additions, - deletions: detail.deletions, - changedFiles: detail.changedFiles, - reviewDecision: detail.reviewDecision, - checksState: detail.checksState, - mergeability: detail.mergeability, - }); - }), - ), + getPullRequestSummary, getPullRequestDetail, listWorkflowRunsRequiringApproval, diff --git a/apps/server/src/pullRequest/gitHubPullRequestJson.test.ts b/apps/server/src/pullRequest/gitHubPullRequestJson.test.ts index 2c3ac6f7c0..801c439a7d 100644 --- a/apps/server/src/pullRequest/gitHubPullRequestJson.test.ts +++ b/apps/server/src/pullRequest/gitHubPullRequestJson.test.ts @@ -2,6 +2,8 @@ import * as Result from "effect/Result"; import { describe, expect, it } from "vite-plus/test"; import { + buildPullRequestSummariesGraphQlQuery, + decodePullRequestSummariesJson, buildReviewSubmissionJson, buildPullRequestStackMembershipsGraphQlQuery, decodePullRequestStackMembershipsJson, @@ -1708,3 +1710,54 @@ describe("pull request stack membership batches", () => { ); }); }); + +describe("batched pull request summaries", () => { + it("refuses a repository GraphQL cannot address, rather than writing it into the document", () => { + expect( + buildPullRequestSummariesGraphQlQuery([{ repository: 'acme/web") { x } #', number: 1 }]), + ).toBeNull(); + expect( + buildPullRequestSummariesGraphQlQuery([{ repository: "acme/web", number: 0 }]), + ).toBeNull(); + expect(buildPullRequestSummariesGraphQlQuery([])).toBeNull(); + }); + + it("files each answer by its alias and skips what GitHub or the decoder could not give", () => { + const decoded = decodePullRequestSummariesJson( + JSON.stringify({ + data: { + s0: { + pullRequest: { + number: 7, + title: "Merged", + url: "https://github.com/acme/web/pull/7", + author: { __typename: "Bot", login: "renovate", avatarUrl: "https://a/r.png" }, + headRefName: "feat/seven", + baseRefName: "main", + state: "MERGED", + mergedAt: "2026-08-24T00:00:00Z", + closedAt: "2026-08-24T00:00:00Z", + updatedAt: "2026-08-24T00:00:00Z", + commits: { nodes: [{ commit: { statusCheckRollup: { state: "FAILURE" } } }] }, + }, + }, + s1: { pullRequest: null }, + s2: { pullRequest: { number: 9 } }, + rateLimit: { cost: 1 }, + }, + }), + ); + expect(Result.isSuccess(decoded)).toBe(true); + if (!Result.isSuccess(decoded)) return; + expect([...decoded.success.keys()]).toEqual([0]); + expect(decoded.success.get(0)).toMatchObject({ + number: 7, + state: "merged", + mergedAt: "2026-08-24T00:00:00Z", + author: { login: "renovate", isBot: true }, + checksState: "failing", + mergeability: "unknown", + additions: 0, + }); + }); +}); diff --git a/apps/server/src/pullRequest/gitHubPullRequestJson.ts b/apps/server/src/pullRequest/gitHubPullRequestJson.ts index 1ebe34ae27..f45416bc91 100644 --- a/apps/server/src/pullRequest/gitHubPullRequestJson.ts +++ b/apps/server/src/pullRequest/gitHubPullRequestJson.ts @@ -1725,6 +1725,139 @@ export function decodePullRequestStatsJson( return Result.succeed(stats); } +/** + * The fields a linked thread keeps current, for many pull requests in one aliased read. Same + * shape as the search row where the two overlap: the checks arrive as GitHub's one-word rollup + * rather than the whole check list `gh pr view` hands back, which is what keeps a batch cheap. + */ +const PULL_REQUEST_SUMMARY_SELECTION = + "number title url state isDraft mergeable reviewDecision additions deletions changedFiles " + + "updatedAt mergedAt closedAt headRefName baseRefName " + + "author { __typename login avatarUrl ... on User { name } } " + + "latestReviews(first: 20) { nodes { state author { login } } } " + + "commits(last: 1) { nodes { commit { statusCheckRollup { state } } } }"; + +/** + * Summaries for pull requests anywhere on one host, one aliased lookup each. Checked and written + * into the document the way `buildPullRequestStatsGraphQlQuery` does, so null means "do not send". + */ +export function buildPullRequestSummariesGraphQlQuery( + changeRequests: ReadonlyArray<{ readonly repository: string; readonly number: number }>, +): string | null { + if (changeRequests.length === 0) return null; + const selections: string[] = []; + for (const [index, changeRequest] of changeRequests.entries()) { + const [owner, name, ...rest] = changeRequest.repository.trim().split("/"); + if (rest.length > 0 || owner === undefined || name === undefined) return null; + if (!REPOSITORY_PART.test(owner) || !REPOSITORY_PART.test(name)) return null; + if (!Number.isSafeInteger(changeRequest.number) || changeRequest.number <= 0) return null; + selections.push( + ` s${index}: repository(owner: "${owner}", name: "${name}") { pullRequest(number: ${changeRequest.number}) { ${PULL_REQUEST_SUMMARY_SELECTION} } }`, + ); + } + return `query PullRequestSummaries {\n${selections.join("\n")}\n}`; +} + +const RawSummarySchema = Schema.Struct({ + ...RawSearchItemSchema.fields, + changedFiles: Schema.optional(Schema.NullOr(Schema.Int)), + additions: Schema.optional(Schema.NullOr(Schema.Int)), + deletions: Schema.optional(Schema.NullOr(Schema.Int)), + closedAt: Schema.optional(Schema.NullOr(Schema.String)), + createdAt: Schema.optional(Schema.String), +}); +const decodeSummaries = decodeJsonResult( + Schema.Struct({ + errors: Schema.optional(Schema.Array(Schema.Unknown)), + data: Schema.optional( + Schema.NullOr( + Schema.Record( + Schema.String, + Schema.NullOr(Schema.Struct({ pullRequest: Schema.optional(Schema.Unknown) })), + ), + ), + ), + }), +); +const decodeSummaryEntry = Schema.decodeUnknownExit(RawSummarySchema); + +/** A failed GraphQL document is not a set of missing aliases to retry individually. */ +export class GitHubSummaryBatchUnavailableError extends Error {} + +export interface GitHubPullRequestSummary { + readonly number: number; + readonly title: string; + readonly url: string; + readonly headBranch: string; + readonly baseBranch: string; + readonly state: PullRequestState; + readonly isDraft: boolean; + readonly closedAt: string | null; + readonly mergedAt: string | null; + readonly updatedAt: string; + readonly author: PullRequestActor | null; + readonly additions: number; + readonly deletions: number; + readonly changedFiles: number; + readonly reviewDecision: PullRequestReviewDecision | null; + readonly checksState: PullRequestChecksState | null; + readonly mergeability: PullRequestMergeability; +} + +/** + * Summaries by the position they were asked in. A pull request GitHub answered nothing for, or + * one whose fields no longer decode, is absent rather than failing the rest of the batch. + */ +export function decodePullRequestSummariesJson( + raw: string, +): Result.Result< + ReadonlyMap, + DecodeFailure | GitHubSummaryBatchUnavailableError +> { + const decoded = decodeSummaries(raw); + if (!Result.isSuccess(decoded)) return Result.fail(decoded.failure); + if (decoded.success.data == null && (decoded.success.errors?.length ?? 0) > 0) { + return Result.fail(new GitHubSummaryBatchUnavailableError("GitHub rejected the summary batch")); + } + const summaries = new Map(); + for (const [alias, value] of Object.entries(decoded.success.data ?? {})) { + const index = /^s(\d+)$/.exec(alias)?.[1]; + if (index === undefined || value?.pullRequest == null) continue; + const entry = decodeSummaryEntry(value.pullRequest); + if (!Exit.isSuccess(entry)) continue; + const pr = entry.value; + summaries.set(Number(index), { + number: pr.number, + title: pr.title, + url: pr.url, + headBranch: pr.headRefName, + baseBranch: pr.baseRefName, + state: toState(pr), + isDraft: pr.isDraft ?? false, + closedAt: trimmed(pr.closedAt), + mergedAt: trimmed(pr.mergedAt), + updatedAt: pr.updatedAt, + author: toActor(pr.author), + additions: pr.additions ?? 0, + deletions: pr.deletions ?? 0, + changedFiles: pr.changedFiles ?? 0, + reviewDecision: toReviewDecisionWithReviews( + pr.reviewDecision, + (pr.latestReviews?.nodes ?? []).flatMap((review) => (review === null ? [] : [review])), + ), + // One enum for the head commit, dressed as a single check like the search row's. + checksState: rollupChecksState( + (pr.commits?.nodes ?? []).flatMap((commitNode) => { + const state = trimmed(commitNode?.commit?.statusCheckRollup?.state); + return state === null ? [] : [{ state }]; + }), + ), + mergeability: toMergeability(pr.mergeable), + }); + } + return Result.succeed(summaries); +} + export function decodePullRequestDetailJson( raw: string, ): Result.Result { From b264cfb2dac2d8aacd1259be9e93f2e4eb3e2757 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:45:15 -0600 Subject: [PATCH 023/104] fix(cursor): preserve internal agent errors (#754) --- apps/server/src/provider/acp/CursorTransportFailure.test.ts | 2 ++ apps/server/src/provider/acp/CursorTransportFailure.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/acp/CursorTransportFailure.test.ts b/apps/server/src/provider/acp/CursorTransportFailure.test.ts index dcd4f9996b..fdde19b934 100644 --- a/apps/server/src/provider/acp/CursorTransportFailure.test.ts +++ b/apps/server/src/provider/acp/CursorTransportFailure.test.ts @@ -38,6 +38,8 @@ describe("CursorTransportFailure", () => { "Error: ConnectError: [unauthenticated] sign in", "Error: ConnectError: [permission_denied] subscription required", "Error: HTTP 500 from the application being debugged", + "Error: RetriableError: [internal] Failed to run step, exceeded max retries", + "Error: RetriableError: [internal] Failed to run step, exceeded max retries\n at step (cli.js:1:2)", ])("preserves prose, code and non-transport errors: %s", (message) => { expect(failureFor([...message])).toBeUndefined(); }); diff --git a/apps/server/src/provider/acp/CursorTransportFailure.ts b/apps/server/src/provider/acp/CursorTransportFailure.ts index 99765c74eb..e6cadb23dc 100644 --- a/apps/server/src/provider/acp/CursorTransportFailure.ts +++ b/apps/server/src/provider/acp/CursorTransportFailure.ts @@ -1,6 +1,7 @@ const maxLineLength = 4096; +// Cursor also uses RetriableError for agent-loop failures; preserve those diagnostics. const transportError = - /^Error: (?:RetriableError: .+|ConnectError: \[(?:unavailable|aborted|deadline_exceeded)\].*)$/; + /^Error: (?:RetriableError: (?!\[internal\]).+|ConnectError: \[(?:unavailable|aborted|deadline_exceeded)\].*)$/; const serverError = "Something went wrong communicating with the server. Please try again."; interface ReplyState { From e82750e5b075c96c31e725f7920bb6ee85f5c97c Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:45:33 -0600 Subject: [PATCH 024/104] fix(ci): compare transfer ceilings only within a scenario (#759) --- .github/scripts/thread-transfer-report.cjs | 4 ++-- .../scripts/thread-transfer-report.test.cjs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/scripts/thread-transfer-report.cjs b/.github/scripts/thread-transfer-report.cjs index 94a02b7806..264a92b1c8 100644 --- a/.github/scripts/thread-transfer-report.cjs +++ b/.github/scripts/thread-transfer-report.cjs @@ -61,7 +61,7 @@ function validateResult(value) { } assertExactKeys(value.scenario, SCENARIO_KEYS, "result.scenario"); - if (value.scenario.id !== "thread-transfer-v1") { + if (!["thread-transfer-v1", "thread-transfer-v2"].includes(value.scenario.id)) { throw new Error("result.scenario.id is not supported"); } for (const key of SCENARIO_KEYS.slice(1)) { @@ -157,7 +157,7 @@ function renderComment(input) { `| ${provider === "codex" ? "Codex" : "Claude"} | ${metric.label} | ${baselineObserved === undefined ? "—" : formatValue(baselineObserved, metric.kind)} | ${formatValue(observed, metric.kind)} | ${formatImpact(observed, baselineObserved, metric.kind)} | ${formatValue(ceiling, metric.kind)} | ${pass ? "✅" : "❌"} |`, ); - if (baseline && baseline.providers[provider].ceiling[metric.key] !== ceiling) { + if (comparable && baseline.providers[provider].ceiling[metric.key] !== ceiling) { ceilingChanges.push( `- ${provider === "codex" ? "Codex" : "Claude"} ${metric.label}: ${formatValue(baseline.providers[provider].ceiling[metric.key], metric.kind)} → ${formatValue(ceiling, metric.kind)}`, ); diff --git a/.github/scripts/thread-transfer-report.test.cjs b/.github/scripts/thread-transfer-report.test.cjs index 4935864e46..3c8ab2578a 100644 --- a/.github/scripts/thread-transfer-report.test.cjs +++ b/.github/scripts/thread-transfer-report.test.cjs @@ -290,3 +290,21 @@ test("preserves a successful result when a same-SHA rerun has no artifact", asyn assert.equal(published, true); assert.equal(updatedComment, false); }); + +test("accepts V2 without comparing it to the V1 scenario", () => { + const current = result(); + current.scenario.id = "thread-transfer-v2"; + current.providers.codex.ceiling = { ...current.providers.codex.ceiling, totalWireBytes: 3000000 }; + assert.equal(validateResult(current), current); + const comment = renderComment({ + current, + baseline: result(), + currentRun: { sha: "bbbbbbbb", conclusion: "success", url: "https://example.com/current" }, + baselineRun: { sha: "aaaaaaaa", matchesBase: true, url: "https://example.com/baseline" }, + }); + assert.match(comment, /fixture changed/); + assert.doesNotMatch(comment, /This PR changes transfer ceilings/); + assert.doesNotMatch(comment, /[+-]\d+\.\d+%/); + current.scenario.id = "unrecognized"; + assert.throws(() => validateResult(current), /not supported/); +}); From 298d788a360903eff340fd0dc0504b954738dad0 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:45:58 -0600 Subject: [PATCH 025/104] fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> --- .agents/upstream-review.md | 2 + apps/server/src/usage/UsageService.test.ts | 261 +++++++++++++++++- apps/server/src/usage/UsageService.ts | 266 +++++++++++++++---- apps/server/src/usage/usageScanCache.test.ts | 77 +----- apps/server/src/usage/usageScanCache.ts | 45 +--- packages/shared/src/usageMerge.test.ts | 46 ++++ packages/shared/src/usageMerge.ts | 12 +- 7 files changed, 531 insertions(+), 178 deletions(-) diff --git a/.agents/upstream-review.md b/.agents/upstream-review.md index 058a7b7dca..00bfa6b5f3 100644 --- a/.agents/upstream-review.md +++ b/.agents/upstream-review.md @@ -221,3 +221,5 @@ The review cursor stays unchanged; unrelated sources in this range remain unclas | PR cache reuse and quota efficiency / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `f4600d77dd7c2fa9f10e8f4500882e427fcc7e26` (#11888), `d612d12b8bb278af97b0029f8d48b2403a5f9ee6` (#12168) | Adopted applicable behavior: scoped cache revisions, canonical detail reuse, deduplicated refreshes and bounded quota probes. Preserve Pylon environment fences and observed GraphQL consumption. Exclude account-router-specific hooks and Forgejo tests: those dependencies are absent in Pylon; their independent source decisions remain open. No cross-environment credential routing introduced. | Cycle [#625](https://github.com/pylon-code/pylon/issues/625), [PR #626](https://github.com/pylon-code/pylon/pull/626); direct adversarial self-review, focused backend/client regressions and scoped types/lint. | | Authenticated GitHub PR media / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `32e8b2584556c0c55ce0d1d8f72b506cb771d60d` (#11706) | Adopted with Pylon path/context guards retained. Signed assets use the active server GitHub CLI account per request; bounded GitHub/CDN redirects strip credentials off credentialed hosts, stream ranges, constrain MIME and sandbox SVG. Older servers retain public fallback. GitHub Enterprise media unsupported. | Cycle [#625](https://github.com/pylon-code/pylon/issues/625), [PR #629](https://github.com/pylon-code/pylon/pull/629); direct adversarial self-review, credential/redirect/range regressions, scoped checks and browser evidence. | + +| Retained usage totals and newest shared scan / `7ed3f711caa36485001538bbe1cb0017b84aa7fe` | `d4d5d12e8ba086cfbf79ca3adeb4156b46ead665` (#10315), `675869d2b0f9e059d5950b80390c0cad1cb1590e` (#12304) | Adopted with Pylon adaptation: retain saved usage after transcript cleanup or moves and across restart; claim a shared fingerprint from the newest scan with a stable ID tie-break. Configured and standalone Antigravity homes retain their profile identity, partial status and per-directory SQLite limits. Stale source identities expire when no current or retained records use them. Existing provider-level wire buckets cannot split costs by individual home when two environments each own distinct and shared homes; that cross-environment overlap remains outside this port and needs source-attributed buckets. The 90-day retention bound remains. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #762](https://github.com/pylon-code/pylon/pull/762); 46 focused tests and scoped server checks. | diff --git a/apps/server/src/usage/UsageService.test.ts b/apps/server/src/usage/UsageService.test.ts index ec1c88ccfd..7b0dcb44df 100644 --- a/apps/server/src/usage/UsageService.test.ts +++ b/apps/server/src/usage/UsageService.test.ts @@ -8,7 +8,9 @@ import * as NodeSqlite from "node:sqlite"; import { assert, describe, it } from "@effect/vitest"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { HostProcessEnvironment } from "@t3tools/shared/hostProcess"; +import { mergeUsage } from "@t3tools/shared/usageMerge"; import { + EnvironmentId, ProviderDriverKind, ProviderInstanceId, ServerProviderInstancesMutationId, @@ -148,14 +150,15 @@ describe("UsageService", () => { [ { type: "session_meta", payload: { id: "codex-account-session" } }, { type: "turn_context", payload: { model: "gpt-5.6-sol" } }, - { + // A-B-A at one timestamp must preserve both equal A events. + ...[11, 12, 11].map((outputTokens) => ({ type: "event_msg", timestamp: "2026-08-01T10:00:00Z", payload: { type: "token_count", - info: { last_token_usage: { input_tokens: 10, output_tokens: 11 } }, + info: { last_token_usage: { input_tokens: 10, output_tokens: outputTokens } }, }, - }, + })), ] .map((line) => encodeUnknownJsonString(line)) .join("\n") + "\n", @@ -215,7 +218,21 @@ describe("UsageService", () => { ), ); const summary = yield* service.readSummary(WINDOW); - assert.strictEqual(totalOutputTokens(summary), 36); + assert.strictEqual(totalOutputTokens(summary), 59); + yield* Effect.promise(() => + NodeFSP.rename( + NodePath.join(codexHome, "sessions", "rollout.jsonl"), + NodePath.join(codexHome, "sessions", "moved.jsonl"), + ), + ); + const moved = yield* service.readSummary(WINDOW); + assert.deepStrictEqual(moved.buckets, summary.buckets); + yield* Effect.promise(() => + NodeFSP.rm(NodePath.join(codexHome, "sessions"), { recursive: true }), + ); + const removed = yield* service.readSummary(WINDOW); + assert.deepStrictEqual(removed.buckets, summary.buckets); + const sources = summary.sources.filter((source) => source.status === "ok"); assert.strictEqual(sources.length, 4); assert.strictEqual( @@ -458,6 +475,128 @@ describe("UsageService", () => { }).pipe(Effect.scoped), ); + it.live("preserves saved tokens, costs and sessions after transcript cleanup and restart", () => + Effect.gen(function* () { + const { transcript, settings, home } = yield* setup; + const alias = NodePath.join(home, "claude-alias"); + yield* Effect.promise(() => + NodeFSP.symlink(NodePath.join(home, "claude"), alias, "junction"), + ); + const content = claudeLine(1, 5); + yield* Effect.promise(() => NodeFSP.writeFile(transcript, content)); + yield* Effect.gen(function* () { + const service = yield* UsageService.make; + const first = yield* service.readSummary(WINDOW); + assert.strictEqual(totalOutputTokens(first), 5); + assert.isAbove(first.buckets[0]?.costUsd ?? 0, 0); + + yield* Effect.promise(() => NodeFSP.rm(transcript)); + const deleted = yield* service.readSummary(WINDOW); + assert.deepStrictEqual(deleted.buckets, first.buckets); + assert.deepStrictEqual(deleted.sources, first.sources); + + const restarted = yield* UsageService.make; + const restored = yield* restarted.readSummary(WINDOW); + assert.deepStrictEqual(restored.buckets, first.buckets); + assert.deepStrictEqual(restored.sources, first.sources); + + // A moved transcript must not count the saved usage twice. + yield* Effect.promise(() => NodeFSP.writeFile(transcript + ".jsonl", content)); + const moved = yield* restarted.readSummary(WINDOW); + assert.deepStrictEqual(moved.buckets, first.buckets); + assert.strictEqual(moved.sources[0]?.distinctSessions, 1); + + const replacementProjects = NodePath.join(home, "replacement-projects"); + yield* Effect.promise(() => NodeFSP.mkdir(replacementProjects)); + yield* Effect.promise(() => + NodeFSP.rm(NodePath.join(home, "claude", "projects"), { recursive: true }), + ); + const afterRootCleanup = yield* UsageService.make; + const missingRoot = yield* afterRootCleanup.readSummary(WINDOW); + assert.deepStrictEqual(missingRoot.buckets, first.buckets); + assert.strictEqual(missingRoot.sources[0]?.distinctSessions, 1); + assert.strictEqual(missingRoot.sources[0]?.status, "ok"); + assert.deepStrictEqual(missingRoot.sources[0]?.fingerprint, first.sources[0]?.fingerprint); + yield* Effect.promise(async () => { + const projects = NodePath.join(home, "claude", "projects"); + await NodeFSP.rename(replacementProjects, projects); + await NodeFSP.writeFile(NodePath.join(projects, "new.jsonl"), claudeLine(2, 7)); + }); + const recreated = yield* afterRootCleanup.readSummary(WINDOW); + assert.strictEqual(totalOutputTokens(recreated), 12); + assert.deepStrictEqual(recreated.sources[0]?.fingerprint, first.sources[0]?.fingerprint); + + const merged = mergeUsage( + [ + { + environmentId: EnvironmentId.make("cleanup-test"), + label: "test", + summary: recreated, + }, + { + environmentId: EnvironmentId.make("other-environment"), + label: "before cleanup", + summary: first, + }, + ], + missingRoot.contractVersion, + ); + assert.strictEqual(merged.outputTokens, 12); + assert.strictEqual(merged.sessions, 1); + assert.strictEqual(merged.costUsd, recreated.buckets[0]?.costUsd); + + const outsideWindow = yield* restarted.readSummary({ + ...WINDOW, + sinceDay: UsageDay.make("2026-08-02"), + }); + assert.deepStrictEqual(outsideWindow.buckets, []); + assert.strictEqual(outsideWindow.sources[0]?.distinctSessions, 0); + }).pipe( + Effect.provide( + serviceLayers({ + prefix: "usage-service-cleanup-test", + home, + settings: { providers: { ...settings.providers, claudeAgent: { homePath: alias } } }, + ratesDocument: { + "claude-fable-5": { input_cost_per_token: 1e-5, output_cost_per_token: 5e-5 }, + }, + }), + ), + ); + }).pipe(Effect.scoped), + ); + + it.live("drops stale source identities after an empty account home is replaced", () => + Effect.gen(function* () { + const { home, settings } = yield* setup; + const replacement = NodePath.join(home, "replacement-claude"); + yield* Effect.promise(() => + NodeFSP.mkdir(NodePath.join(replacement, "projects"), { recursive: true }), + ); + yield* Effect.gen(function* () { + const config = yield* ServerConfig.ServerConfig; + const settingsService = yield* ServerSettings.ServerSettingsService; + const service = yield* UsageService.make; + yield* service.readSummary(WINDOW); + const cachePath = NodePath.join(config.stateDir, "usage-scan-cache.json"); + const oldDir = NodePath.join(home, "claude", "projects"); + assert.include(yield* Effect.promise(() => NodeFSP.readFile(cachePath, "utf8")), oldDir); + + yield* settingsService.updateSettings({ + providers: { claudeAgent: { homePath: replacement } }, + }); + yield* service.readSummary(WINDOW); + const persisted = yield* Effect.promise(() => NodeFSP.readFile(cachePath, "utf8")); + assert.notInclude(persisted, oldDir); + assert.include(persisted, NodePath.join(replacement, "projects")); + }).pipe( + Effect.provide( + serviceLayers({ prefix: "usage-service-source-prune-test", home, settings }), + ), + ); + }).pipe(Effect.scoped), + ); + it.live("does not share an in-flight scan after custom prices change", () => Effect.gen(function* () { const { transcript, settings, home } = yield* setup; @@ -769,6 +908,120 @@ describe("UsageService", () => { ), ); + it.live("retains Antigravity usage after conversation cleanup and service restart", () => + Effect.gen(function* () { + yield* setup; + const config = yield* ServerConfig.ServerConfig; + const instanceId = ProviderInstanceId.make("antigravity-retained"); + const profileDir = resolveAntigravityProfileDirectory(config.stateDir, instanceId); + const convDir = NodePath.join(profileDir, "antigravity-acp", "conversations"); + yield* Effect.promise(() => NodeFSP.mkdir(convDir, { recursive: true })); + const dbPath = NodePath.join(convDir, "retained.db"); + seedAntigravityDb(dbPath, [ + { + idx: 0, + blob: encodeSyntheticGenMetadataBlob({ + modelName: "gemini-2.5-pro", + timestampSeconds: 1785578400n, + inputTokens: 100, + outputTokens: 40, + }), + }, + ]); + + const service = yield* UsageService.make; + const first = yield* service.readSummary(WINDOW); + yield* Effect.promise(() => NodeFSP.rm(convDir, { recursive: true })); + const afterCleanup = yield* service.readSummary(WINDOW); + const restarted = yield* UsageService.make; + const afterRestart = yield* restarted.readSummary(WINDOW); + + for (const [phase, summary] of [ + ["cleanup", afterCleanup], + ["restart", afterRestart], + ] as const) { + assert.deepStrictEqual(summary.buckets, first.buckets, phase); + const source = summary.sources.find( + (item) => + item.fingerprint.provider === "antigravity" && + item.fingerprint.resolvedHomePath === convDir, + ); + assert.isDefined(source); + assert.strictEqual(source.status, "ok"); + assert.strictEqual(source.scannedFiles, 1); + } + }).pipe( + Effect.provide( + serviceLayers({ + prefix: "usage-service-antigravity-retained-test", + home: "", + settings: { + providers: {}, + providerInstances: { + [ProviderInstanceId.make("antigravity-retained")]: { + driver: "antigravity", + enabled: true, + }, + }, + }, + }), + ), + Effect.scoped, + ), + ); + + it.live( + "retains standalone Antigravity history after its directory is removed and service restarts", + () => + Effect.gen(function* () { + yield* setup; + const config = yield* ServerConfig.ServerConfig; + const convDir = NodePath.join(config.stateDir, "standalone-antigravity", "conversations"); + yield* Effect.promise(() => NodeFSP.mkdir(convDir, { recursive: true })); + seedAntigravityDb(NodePath.join(convDir, "session.db"), [ + { + idx: 0, + blob: encodeSyntheticGenMetadataBlob({ + modelName: "gemini-2.5-pro", + timestampSeconds: 1785578400n, + inputTokens: 100, + outputTokens: 40, + }), + }, + ]); + + yield* Effect.gen(function* () { + const firstService = yield* UsageService.make; + const first = yield* firstService.readSummary(WINDOW); + assert.isTrue(first.buckets.some((bucket) => bucket.provider === "antigravity")); + yield* Effect.promise(() => NodeFSP.rm(convDir, { recursive: true })); + + for (const summary of [ + yield* firstService.readSummary(WINDOW), + yield* (yield* UsageService.make).readSummary(WINDOW), + ]) { + assert.deepStrictEqual(summary.buckets, first.buckets); + const source = summary.sources.find( + (item) => + item.fingerprint.provider === "antigravity" && + item.fingerprint.resolvedHomePath === convDir, + ); + assert.isDefined(source); + assert.strictEqual(source.status, "ok"); + } + }).pipe(Effect.provideService(UsageService.StandaloneAntigravityConversations, convDir)); + }).pipe( + Effect.provide( + serviceLayers({ + prefix: "usage-service-standalone-antigravity-retained-test", + home: "", + settings: { providers: {}, providerInstances: {} }, + }), + ), + Effect.scoped, + ), + ); + it.live( "reports honest missing status when configured Antigravity instance has no conversations dir on disk", () => diff --git a/apps/server/src/usage/UsageService.ts b/apps/server/src/usage/UsageService.ts index 122b89436e..8d63a91b9d 100644 --- a/apps/server/src/usage/UsageService.ts +++ b/apps/server/src/usage/UsageService.ts @@ -14,6 +14,8 @@ * @module UsageService */ import * as NodeOS from "node:os"; +// @effect-diagnostics nodeBuiltinImport:off - Context.Reference defaults cannot request Path. +import * as NodePath from "node:path"; import { ClaudeSettings, @@ -90,6 +92,18 @@ const MAX_HOURLY_WINDOW_MS = 24 * 60 * 60 * 1000; /** Longest window the UI offers, plus slack. Older entries are pruned. */ const CACHE_RETENTION_DAYS = 90; +const MAX_ANTIGRAVITY_FILES_PER_DIR = 500; +const MAX_ANTIGRAVITY_BYTES_PER_DIR = 256 * 1024 * 1024; +const MAX_ANTIGRAVITY_RECORDS_PER_DIR = 50_000; + +/** The standalone Antigravity home belongs to this server environment. */ +export const StandaloneAntigravityConversations = Context.Reference( + "StandaloneAntigravityConversations", + { + defaultValue: () => + NodePath.join(NodeOS.homedir(), ".gemini", "antigravity-cli", "conversations"), + }, +); const decodeCodexSettings = Schema.decodeOption(CodexSettings); const decodeClaudeSettings = Schema.decodeOption(ClaudeSettings); @@ -110,6 +124,11 @@ const encodeRatesCache = Schema.encodeEffect( const ScanCacheJson = Schema.fromJsonString(Schema.Unknown as unknown as Schema.Codec); const decodeScanCacheFile = Schema.decodeUnknownEffect(ScanCacheJson); const encodeScanCacheFile = Schema.encodeEffect(ScanCacheJson); +const encodeUsageRecordKey = Schema.encodeSync(ScanCacheJson); +const CachedSource = Schema.Struct({ dir: Schema.String, volumeId: Schema.String }); +const decodeCachedSources = Schema.decodeUnknownOption( + Schema.Struct({ sources: Schema.Record(Schema.String, CachedSource) }), +); export class UsageService extends Context.Service< UsageService, @@ -156,7 +175,12 @@ export const make = Effect.gen(function* () { const hostEnvironment = yield* HostProcessEnvironment; const fileCache: ScanCache = new Map(); + const sourceCache = new Map(); let cacheDirty = false; + const isWithinDirectory = (filePath: string, dir: string) => { + const relative = path.relative(dir, filePath); + return relative !== ".." && !relative.startsWith(".." + path.sep) && !path.isAbsolute(relative); + }; const ratesCachePath = path.join(config.stateDir, "usage-model-rates.json"); const scanCachePath = path.join(config.stateDir, "usage-scan-cache.json"); @@ -250,9 +274,16 @@ export const make = Effect.gen(function* () { /** Resolves the transcript directory for each provider. */ const resolveTranscriptDirs = Effect.fn("UsageService.resolveTranscriptDirs")(function* ( settings: ServerSettingsValue, + retentionCutoffMs: number, ) { - const dirs: Array<{ provider: UsageProviderKind; dir: string; fileName?: string }> = []; + const dirs: Array<{ + provider: UsageProviderKind; + dir: string; + volumeId: string; + fileName?: string; + }> = []; const seen = new Set(); + const activeSourceKeys = new Set(); for (const driver of ["claudeAgent", "codex", "grok"] as const) { // Disabled accounts still have history. Explicit default slots replace // the legacy settings, just as they do in the provider registry. @@ -289,14 +320,42 @@ export const make = Effect.gen(function* () { ); } const directory = path.resolve(home, provider === "claude" ? "projects" : "sessions"); - // Account aliases and Codex auth overlays can share the same history. + const sourceKey = provider + "\0" + directory; + const previous = sourceCache.get(sourceKey); + // Keep canonical paths and source fingerprints stable after root cleanup, + // including aliases and clients merging pre-cleanup environment summaries. const dir = yield* fileSystem .realPath(directory) - .pipe(Effect.orElseSucceed(() => directory)); + .pipe(Effect.orElseSucceed(() => previous?.dir ?? directory)); + const currentVolumeId = yield* Effect.promise(() => readDirectoryVolumeId(dir)); + const hasRetainedHistory = fileCache + .entries() + .some( + ([filePath, entry]) => + entry.provider === provider && + entry.mtimeMs >= retentionCutoffMs && + entry.records.length + entry.tailRecords.length > 0 && + isWithinDirectory(filePath, dir), + ); + // A recreated directory still reports the retained history under its old identity. + const volumeId = + previous?.dir === dir && (hasRetainedHistory || !currentVolumeId) + ? previous.volumeId || currentVolumeId + : currentVolumeId; + if (previous?.dir !== dir || previous.volumeId !== volumeId) { + sourceCache.set(sourceKey, { dir, volumeId }); + cacheDirty = true; + } const key = `${provider}\0${dir}`; + activeSourceKeys.add(sourceKey); if (seen.has(key)) continue; seen.add(key); - dirs.push({ provider, dir, ...(provider === "grok" ? { fileName: "updates.jsonl" } : {}) }); + dirs.push({ + provider, + dir, + volumeId, + ...(provider === "grok" ? { fileName: "updates.jsonl" } : {}), + }); } } // Antigravity profile roots: resolve configured instances @@ -324,21 +383,69 @@ export const make = Effect.gen(function* () { } // Standalone ~/.gemini/antigravity-cli/conversations when present - const standaloneDir = path.join( - NodeOS.homedir(), - ".gemini", - "antigravity-cli", - "conversations", - ); + const standaloneDir = yield* StandaloneAntigravityConversations; const standaloneExists = yield* fileSystem .exists(standaloneDir) .pipe(Effect.catchCause(() => Effect.succeed(false))); - if (standaloneExists) { + const hasStandaloneHistory = fileCache + .entries() + .some( + ([filePath, entry]) => + entry.provider === "antigravity" && + entry.mtimeMs >= retentionCutoffMs && + entry.records.length + entry.tailRecords.length > 0 && + isWithinDirectory(filePath, standaloneDir), + ); + if (standaloneExists || hasStandaloneHistory) { antigravityRoots.add(standaloneDir); } for (const root of antigravityRoots) { - dirs.push({ provider: "antigravity" as const, dir: root }); + const directory = path.resolve(root); + const sourceKey = "antigravity\0" + directory; + const previous = sourceCache.get(sourceKey); + // Pylon's Antigravity profile fingerprint is the configured profile + // path, not its realpath. Keep it stable across profile cleanup. + const dir = directory; + const currentVolumeId = yield* Effect.promise(() => readDirectoryVolumeId(dir)); + const hasRetainedHistory = fileCache + .entries() + .some( + ([filePath, entry]) => + entry.provider === "antigravity" && + entry.mtimeMs >= retentionCutoffMs && + entry.records.length + entry.tailRecords.length > 0 && + isWithinDirectory(filePath, dir), + ); + const volumeId = + previous?.dir === dir && (hasRetainedHistory || !currentVolumeId) + ? previous.volumeId || currentVolumeId + : currentVolumeId; + if (previous?.dir !== dir || previous.volumeId !== volumeId) { + sourceCache.set(sourceKey, { dir, volumeId }); + cacheDirty = true; + } + activeSourceKeys.add(sourceKey); + dirs.push({ provider: "antigravity", dir, volumeId }); + } + + // Old configured homes can disappear from settings. Keep their identity + // only while an in-retention cached transcript could use it again. + for (const [sourceKey, source] of sourceCache) { + if (activeSourceKeys.has(sourceKey)) continue; + const provider = sourceKey.split("\0", 1)[0]; + const hasRetainedHistory = fileCache + .entries() + .some( + ([filePath, entry]) => + entry.provider === provider && + entry.mtimeMs >= retentionCutoffMs && + entry.records.length + entry.tailRecords.length > 0 && + isWithinDirectory(filePath, source.dir), + ); + if (hasRetainedHistory) continue; + sourceCache.delete(sourceKey); + cacheDirty = true; } return dirs; @@ -359,6 +466,11 @@ export const make = Effect.gen(function* () { ); if (document === null) return; for (const [path, entry] of decodeScanCache(document)) fileCache.set(path, entry); + const sources = decodeCachedSources(document); + if (Option.isSome(sources)) { + for (const [key, source] of Object.entries(sources.value.sources)) + sourceCache.set(key, source); + } }), ); @@ -366,7 +478,10 @@ export const make = Effect.gen(function* () { if (!cacheDirty) return; // Cleared only after the write lands, so a failed persist is retried on // the next scan instead of leaving disk permanently stale. - yield* encodeScanCacheFile(encodeScanCache(fileCache)).pipe( + yield* encodeScanCacheFile({ + ...encodeScanCache(fileCache), + sources: Object.fromEntries(sourceCache), + }).pipe( Effect.flatMap((serialized) => fileSystem.writeFileString(scanCachePath, serialized)), Effect.map(() => { cacheDirty = false; @@ -417,7 +532,8 @@ export const make = Effect.gen(function* () { ); // A read failure is not an empty transcript: caching it under this // (size, mtime) would silently drop the file's usage until it changes. - if (parsed === null) return []; + if (parsed === null) + return cached?.provider === provider ? [...cached.records, ...cached.tailRecords] : []; // Stored already de-duplicated within the file, which is 99% of all // duplicates. The aggregator still runs the cross-file dedupe pass. One @@ -452,24 +568,25 @@ export const make = Effect.gen(function* () { readonly status?: "ok" | "missing" | "partial" | "failed"; readonly malformedRecords?: number; readonly message?: string | null; + readonly nativeBudget?: { + readonly files: number; + readonly bytes: number; + readonly records: number; + }; } const collectDirs = Effect.fn("UsageService.collectDirs")(function* ( windowStartMs: number, settings: ServerSettingsValue, + retentionCutoffMs: number, ) { // The home resolvers ask for `Path` themselves; satisfy them from the // instance we already hold so the scan stays context-free. - const dirs = yield* resolveTranscriptDirs(settings).pipe( + const dirs = yield* resolveTranscriptDirs(settings, retentionCutoffMs).pipe( Effect.provideService(Path.Path, path), ); const scanned: ScannedDir[] = []; - // Bound native SQLite work across all instance roots, including warm-cache records. - let nativeFiles = 0; - let nativeBytes = 0; - let nativeRecords = 0; - for (const { provider, dir, fileName } of dirs) { - const volumeId = yield* Effect.promise(() => readDirectoryVolumeId(dir)); + for (const { provider, dir, volumeId, fileName } of dirs) { const exists = yield* fileSystem .exists(dir) .pipe(Effect.catchCause(() => Effect.succeed(false))); @@ -479,6 +596,10 @@ export const make = Effect.gen(function* () { } if (provider === "antigravity") { + // Native SQLite limits apply to each profile directory independently. + let nativeFiles = 0; + let nativeBytes = 0; + let nativeRecords = 0; const dirEntries = yield* fileSystem .readDirectory(dir) .pipe(Effect.catchCause(() => Effect.succeed(null))); @@ -497,10 +618,6 @@ export const make = Effect.gen(function* () { const dbFileNames = dirEntries.filter((name) => name.endsWith(".db")).sort(); - const MAX_ANTIGRAVITY_FILES_PER_DIR = 500; - const MAX_ANTIGRAVITY_BYTES_PER_DIR = 256 * 1024 * 1024; - const MAX_ANTIGRAVITY_RECORDS_PER_DIR = 50_000; - const parsedFiles: { path: string; records: readonly UsageRecord[] }[] = []; let dirMalformedRows = 0; let dirTruncated = false; @@ -683,6 +800,7 @@ export const make = Effect.gen(function* () { status, malformedRecords: dirMalformedRows, message, + nativeBudget: { files: nativeFiles, bytes: nativeBytes, records: nativeRecords }, }); continue; } @@ -749,11 +867,13 @@ export const make = Effect.gen(function* () { const windowStartMs = (hourlyWindow?.sinceTimeMs ?? DateTime.toEpochMillis(windowStart.value)) - MTIME_SLACK_MS; + const retentionCutoffMs = startedAtMs - CACHE_RETENTION_DAYS * 24 * 60 * 60 * 1000; + // Pricing only matters once records are aggregated, so the rate table // loads while transcripts stream instead of gating them: a cold rates // fetch on a slow network no longer delays the scan by its own timeout. const [, scannedDirs] = yield* Effect.all( - [ensureRates(false), collectDirs(windowStartMs, settings)], + [ensureRates(false), collectDirs(windowStartMs, settings, retentionCutoffMs)], { concurrency: 2 }, ); @@ -768,8 +888,6 @@ export const make = Effect.gen(function* () { }); const sources: UsageSource[] = []; - const livePaths = new Set(); - const walkedRoots: string[] = []; for (const { provider, @@ -779,38 +897,72 @@ export const make = Effect.gen(function* () { status, malformedRecords, message, + nativeBudget, } of scannedDirs) { - if (files === null) { - sources.push({ - fingerprint: { hostId, provider, resolvedHomePath: dir, volumeId }, - status: "missing", - scannedFiles: 0, - skippedFiles: 0, - malformedRecords: 0, - distinctSessions: 0, - message: "No transcript directory on this environment.", - }); - continue; + const retainedFiles = [...(files ?? [])]; + let retainedTruncated = false; + let retainedNativeFiles = nativeBudget?.files ?? 0; + let retainedNativeBytes = nativeBudget?.bytes ?? 0; + let retainedNativeRecords = nativeBudget?.records ?? 0; + const livePaths = new Set(retainedFiles.map((file) => file.path)); + // Cleanup may remove transcripts, but the usage we already saved still + // contributes to this source. Keep the normal aggregation and dedupe path. + for (const [filePath, entry] of fileCache) { + if ( + entry.provider !== provider || + entry.mtimeMs < retentionCutoffMs || + livePaths.has(filePath) || + !isWithinDirectory(filePath, dir) + ) + continue; + if (provider === "antigravity") { + const recordCount = entry.records.length + entry.tailRecords.length; + if ( + retainedNativeFiles + 1 > MAX_ANTIGRAVITY_FILES_PER_DIR || + retainedNativeBytes + entry.size > MAX_ANTIGRAVITY_BYTES_PER_DIR || + retainedNativeRecords + recordCount > MAX_ANTIGRAVITY_RECORDS_PER_DIR + ) { + retainedTruncated = true; + continue; + } + retainedNativeFiles++; + retainedNativeBytes += entry.size; + retainedNativeRecords += recordCount; + } + retainedFiles.push({ path: filePath, records: [...entry.records, ...entry.tailRecords] }); } - - walkedRoots.push(dir); let scannedFiles = 0; let skippedFiles = 0; // Distinct per directory. Buckets carry per-cell session counts, but a // session spans days and models, so clients total this figure instead. const sessionIds = new Set(); - for (const file of files) { - livePaths.add(file.path); + for (const file of retainedFiles) { if (file.records.length === 0) { skippedFiles += 1; continue; } scannedFiles += 1; + const codexEventOccurrences = new Map(); for (const record of file.records) { - // Only sessions that contributed in-window count: the mtime slack - // admits boundary files whose records fall outside the range. - if (aggregator.add(record) && record.sessionId.length > 0) { + let usageRecord = record; + if (record.provider === "codex" && record.sessionId.length > 0) { + // Match moved rollout copies without collapsing repeated equal events + // within one rollout (timestamps can have only second precision). + const key = encodeUsageRecordKey([ + record.provider, + record.sessionId, + record.timestampMs, + record.model, + record.totals, + ]); + const occurrence = (codexEventOccurrences.get(key) ?? 0) + 1; + codexEventOccurrences.set(key, occurrence); + usageRecord = { ...record, dedupeKey: key + ":" + occurrence }; + } + // Only sessions contributing in-window count; the mtime slack can + // admit boundary files whose records fall outside the range. + if (aggregator.add(usageRecord) && record.sessionId.length > 0) { sessionIds.add(record.sessionId); } } @@ -818,21 +970,27 @@ export const make = Effect.gen(function* () { sources.push({ fingerprint: { hostId, provider, resolvedHomePath: dir, volumeId }, - status: status ?? "ok", + // Saved records remain available after cleanup. Preserve Pylon's native + // partial/failed scan status when an Antigravity read was incomplete. + status: retainedTruncated + ? "partial" + : files === null + ? scannedFiles === 0 + ? "missing" + : "ok" + : status === "failed" && scannedFiles > 0 + ? "partial" + : (status ?? "ok"), scannedFiles, skippedFiles, malformedRecords: malformedRecords ?? 0, distinctSessions: sessionIds.size, - message: message ?? null, + message: + files === null ? "No transcript directory on this environment." : (message ?? null), }); } - const pruned = pruneScanCache(fileCache, { - livePaths, - walkedRoots, - windowStartMs, - retentionCutoffMs: startedAtMs - CACHE_RETENTION_DAYS * 24 * 60 * 60 * 1000, - }); + const pruned = pruneScanCache(fileCache, retentionCutoffMs); if (pruned > 0) cacheDirty = true; yield* persistScanCache(); diff --git a/apps/server/src/usage/usageScanCache.test.ts b/apps/server/src/usage/usageScanCache.test.ts index fdb0aabafa..cc1bdbcc16 100644 --- a/apps/server/src/usage/usageScanCache.test.ts +++ b/apps/server/src/usage/usageScanCache.test.ts @@ -194,88 +194,17 @@ describe("pruneScanCache", () => { it("drops entries older than retention", () => { const cache = cacheWith([["/old.jsonl", 500, [record()]]]); - const removed = pruneScanCache(cache, { - livePaths: new Set(), - walkedRoots: ["/"], - windowStartMs: 400, - retentionCutoffMs, - }); + const removed = pruneScanCache(cache, retentionCutoffMs); expect(removed).toBe(1); expect(cache.size).toBe(0); }); - it("drops in-window entries whose file has disappeared", () => { + it("keeps entries whose file has disappeared", () => { const cache = cacheWith([["/gone.jsonl", 5000, [record()]]]); - pruneScanCache(cache, { - livePaths: new Set(), - walkedRoots: ["/"], - windowStartMs: 4000, - retentionCutoffMs, - }); - - expect(cache.size).toBe(0); - }); - - it("keeps entries outside the walked window that are still within retention", () => { - // Viewing 7 days must not evict the 30-day entries, which that walk never - // looked for and so cannot prove are gone. - const cache = cacheWith([["/older-but-valid.jsonl", 2000, [record()]]]); - - const removed = pruneScanCache(cache, { - livePaths: new Set(), - walkedRoots: ["/"], - windowStartMs: 4000, - retentionCutoffMs, - }); - - expect(removed).toBe(0); - expect(cache.size).toBe(1); - }); - - it("keeps entries the walk saw", () => { - const cache = cacheWith([["/live.jsonl", 5000, [record()]]]); - - pruneScanCache(cache, { - livePaths: new Set(["/live.jsonl"]), - walkedRoots: ["/"], - windowStartMs: 4000, - retentionCutoffMs, - }); - - expect(cache.size).toBe(1); - }); -}); - -describe("pruneScanCache with an unwalked root", () => { - it("keeps in-window entries for a provider whose directory was not walked", () => { - // A missing provider root or failed settings read leaves livePaths without - // that provider's files. Its warm entries must survive the pass. - const cache = cacheWith([["/codex/sessions/a.jsonl", 5000, [record()]]]); - - const removed = pruneScanCache(cache, { - livePaths: new Set(), - walkedRoots: ["/claude/projects"], - windowStartMs: 4000, - retentionCutoffMs: 1000, - }); - - expect(removed).toBe(0); - expect(cache.size).toBe(1); - }); - - it("keeps entries under a sibling path that only shares the walked root prefix", () => { - const cache = cacheWith([["/claude/projects-copy/a.jsonl", 5000, [record()]]]); - - const removed = pruneScanCache(cache, { - livePaths: new Set(), - walkedRoots: ["/claude/projects"], - windowStartMs: 4000, - retentionCutoffMs: 1000, - }); + pruneScanCache(cache, retentionCutoffMs); - expect(removed).toBe(0); expect(cache.size).toBe(1); }); }); diff --git a/apps/server/src/usage/usageScanCache.ts b/apps/server/src/usage/usageScanCache.ts index 04f9597f42..8439d5ca74 100644 --- a/apps/server/src/usage/usageScanCache.ts +++ b/apps/server/src/usage/usageScanCache.ts @@ -14,9 +14,6 @@ * * @module usageScanCache */ -// @effect-diagnostics nodeBuiltinImport:off -import * as NodePath from "node:path"; - import type { UsageProviderKind } from "@t3tools/contracts"; import { GUARD_LENGTH, type TranscriptParsePosition } from "./usageTranscriptReader.ts"; @@ -302,47 +299,11 @@ function decodeCodexState(value: unknown): CodexScanState | null | undefined { }; } -export interface PruneOptions { - /** Files the walk just saw. Only meaningful inside the walked window. */ - readonly livePaths: ReadonlySet; - /** - * Roots the walk actually completed. Absence from `livePaths` only proves a - * file is gone when its root was walked: a provider whose directory failed to - * resolve this pass must not have its warm entries purged. - */ - readonly walkedRoots: readonly string[]; - /** Start of the walked window; entries older than this were not looked for. */ - readonly windowStartMs: number; - /** Entries older than this are dropped regardless. */ - readonly retentionCutoffMs: number; -} - -/** - * Drops aged-out entries, and entries for files that have disappeared. - * - * The walk only covers the requested window, so absence from `livePaths` only - * proves deletion for entries *inside* that window. Pruning everything the walk - * missed would evict the 30-day entries every time someone looked at 7 days. - * - * Replaces an earlier record cap that cleared the whole cache once exceeded, - * which meant a large enough window never warmed up at all. - */ -export function pruneScanCache(cache: ScanCache, options: PruneOptions): number { +/** Keeps saved usage after transcript cleanup, until the reporting retention expires. */ +export function pruneScanCache(cache: ScanCache, retentionCutoffMs: number): number { let removed = 0; for (const [path, entry] of cache) { - const agedOut = entry.mtimeMs < options.retentionCutoffMs; - const underWalkedRoot = options.walkedRoots.some((root) => { - const relative = NodePath.relative(root, path); - return ( - relative === "" || - (relative !== ".." && - !relative.startsWith(`..${NodePath.sep}`) && - !NodePath.isAbsolute(relative)) - ); - }); - const deleted = - underWalkedRoot && entry.mtimeMs >= options.windowStartMs && !options.livePaths.has(path); - if (agedOut || deleted) { + if (entry.mtimeMs < retentionCutoffMs) { cache.delete(path); removed += 1; } diff --git a/packages/shared/src/usageMerge.test.ts b/packages/shared/src/usageMerge.test.ts index 3da3865022..9c1346b3ce 100644 --- a/packages/shared/src/usageMerge.test.ts +++ b/packages/shared/src/usageMerge.test.ts @@ -147,6 +147,52 @@ describe("mergeUsage", () => { ).toEqual({ claude: 1, codex: 1 }); }); + it("uses the newest scan when environments share the same transcript directory", () => { + const source = { provider: "claude" as const, hostId: "mac", homePath: "/home/theo/.claude" }; + const environments = [ + environment("env-a", summary([bucket({ costUsd: 4, records: 2 })], [source])), + environment("env-b", { + ...summary([bucket()], [source]), + readAt: "2026-08-07T01:00:00.000Z", + }), + ]; + + for (const ordered of [environments, environments.toReversed()]) { + const merged = mergeUsage(ordered, USAGE_CONTRACT_VERSION); + expect(merged.costUsd).toBe(10); + expect(merged.records).toBe(5); + expect(merged.sessions).toBe(1); + expect(merged.contributingEnvironments).toEqual(["env-b"]); + expect(merged.duplicateSources).toEqual(["env-a: /home/theo/.claude"]); + } + }); + + it("uses stable environment ids for equal or invalid scan timestamps", () => { + const source = { provider: "claude" as const, hostId: "mac", homePath: "/shared/.claude" }; + const invalid = environment("env-a", { + ...summary([bucket({ costUsd: 4 })], [source]), + readAt: "invalid", + }); + const equallyNew = [ + environment("env-b", summary([bucket({ costUsd: 10 })], [source])), + environment("env-c", summary([bucket({ costUsd: 20 })], [source])), + ]; + for (const ordered of [[invalid, ...equallyNew], [...equallyNew, invalid].toReversed()]) { + const merged = mergeUsage(ordered, USAGE_CONTRACT_VERSION); + expect(merged.costUsd).toBe(10); + expect(merged.contributingEnvironments).toEqual(["env-b"]); + } + const bothInvalid = mergeUsage( + [ + environment("env-z", { ...summary([bucket({ costUsd: 9 })], [source]), readAt: "bad" }), + invalid, + ], + USAGE_CONTRACT_VERSION, + ); + expect(bothInvalid.costUsd).toBe(4); + expect(bothInvalid.contributingEnvironments).toEqual(["env-a"]); + }); + it("excludes an environment reporting an older contract version", () => { const merged = mergeUsage( [ diff --git a/packages/shared/src/usageMerge.ts b/packages/shared/src/usageMerge.ts index e0cb0510eb..fee8e9c956 100644 --- a/packages/shared/src/usageMerge.ts +++ b/packages/shared/src/usageMerge.ts @@ -118,9 +118,9 @@ function fingerprintKey(fingerprint: UsageSourceFingerprint): string { * * Several environments on one machine (worktree servers, for instance) resolve * the same provider home and would otherwise double count every token. The - * first environment in a stable order claims a fingerprint; the rest have that - * provider's buckets dropped. Environments are sorted by id so the winner does - * not change between renders. + * most recently read summary claims a fingerprint; the rest have that provider's + * buckets dropped. Environment ids break ties so the winner is stable when + * summaries have the same read time. */ function claimSources(environments: readonly EnvironmentUsage[]): { readonly ownerByFingerprint: ReadonlyMap; @@ -129,7 +129,11 @@ function claimSources(environments: readonly EnvironmentUsage[]): { const ownerByFingerprint = new Map(); const duplicates: string[] = []; - const ordered = [...environments].sort((a, b) => a.environmentId.localeCompare(b.environmentId)); + const ordered = [...environments].sort( + (a, b) => + (Date.parse(b.summary.readAt) || 0) - (Date.parse(a.summary.readAt) || 0) || + a.environmentId.localeCompare(b.environmentId), + ); for (const environment of ordered) { for (const source of environment.summary.sources) { From e4434e55955b5bbbab5e88c7fb947ba871422a82 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:46:20 -0600 Subject: [PATCH 026/104] fix(ssh): find Linuxbrew Node on WSL hosts (#769) --- packages/ssh/src/tunnel.test.ts | 1 + packages/ssh/src/tunnel.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/ssh/src/tunnel.test.ts b/packages/ssh/src/tunnel.test.ts index 822f260a63..4cfa1c09e4 100644 --- a/packages/ssh/src/tunnel.test.ts +++ b/packages/ssh/src/tunnel.test.ts @@ -118,6 +118,7 @@ describe("ssh tunnel scripts", () => { assert.include(script, "remote_node_satisfies_engine()"); assert.include(script, "function satisfiesSemverRange"); assert.include(script, "satisfiesSemverRange(rawVersion, range)"); + assert.include(script, 'prepend_path_if_dir "/home/linuxbrew/.linuxbrew/bin"'); assert.include(script, 'prepend_path_if_dir "$VOLTA_HOME/bin"'); assert.include(script, 'prepend_path_if_dir "$HOME/.asdf/shims"'); assert.include(script, 'prepend_path_if_dir "$HOME/.local/share/mise/shims"'); diff --git a/packages/ssh/src/tunnel.ts b/packages/ssh/src/tunnel.ts index b88872b087..32e5f4ffaf 100644 --- a/packages/ssh/src/tunnel.ts +++ b/packages/ssh/src/tunnel.ts @@ -336,6 +336,7 @@ ensure_remote_node_path() { prepend_path_if_dir "$HOME/.local/bin" prepend_path_if_dir "$HOME/bin" prepend_path_if_dir "/opt/homebrew/bin" + prepend_path_if_dir "/home/linuxbrew/.linuxbrew/bin" prepend_path_if_dir "/usr/local/bin" prepend_path_if_dir "/usr/bin" prepend_path_if_dir "/bin" From 08e6f51ddef04b85a26cf4bd900f95d182b80c5e Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 10:50:49 -0600 Subject: [PATCH 027/104] fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --- apps/server/src/git/GitManager.test.ts | 17 ++++++++++++++++- apps/server/src/vcs/GitVcsDriverCore.ts | 10 ++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/apps/server/src/git/GitManager.test.ts b/apps/server/src/git/GitManager.test.ts index e6a637bf78..a1b1062666 100644 --- a/apps/server/src/git/GitManager.test.ts +++ b/apps/server/src/git/GitManager.test.ts @@ -4174,7 +4174,7 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { }), ); - it.effect("generates PR content against the remote base when the local base is stale", () => + it.effect("generates PR content from branch changes when the remote base advances", () => Effect.gen(function* () { const repoDir = yield* makeTempDir("t3code-git-manager-"); yield* initRepo(repoDir); @@ -4206,7 +4206,15 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { yield* runGit(repoDir, ["push", "-u", "origin", "feature/remote-base"]); yield* runGit(repoDir, ["config", "branch.feature/remote-base.gh-merge-base", "main"]); + NodeFS.writeFileSync(NodePath.join(peerDir, "later-main.txt"), "unrelated\n"); + yield* runGit(peerDir, ["add", "later-main.txt"]); + yield* runGit(peerDir, ["commit", "-m", "Later main commit"]); + yield* runGit(peerDir, ["push", "origin", "main"]); + yield* runGit(repoDir, ["fetch", "origin"]); + let generatedCommitSummary = ""; + let generatedDiffSummary = ""; + let generatedDiffPatch = ""; const { manager } = yield* makeManager({ ghScenario: { prListSequence: ["[]", "[]"], @@ -4214,6 +4222,8 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { textGeneration: { generatePrContent: (input) => { generatedCommitSummary = input.commitSummary; + generatedDiffSummary = input.diffSummary; + generatedDiffPatch = input.diffPatch; return Effect.succeed({ title: "Feature PR", body: "Feature body" }); }, }, @@ -4227,6 +4237,11 @@ it.layer(GitManagerTestLayer)("GitManager", (it) => { expect(result.pr.status).toBe("created"); expect(generatedCommitSummary).toContain("Feature commit"); expect(generatedCommitSummary).not.toContain("Remote base commit"); + expect(generatedCommitSummary).not.toContain("Later main commit"); + expect(generatedDiffSummary).toContain("feature.txt"); + expect(generatedDiffSummary).not.toContain("later-main.txt"); + expect(generatedDiffPatch).toContain("feature.txt"); + expect(generatedDiffPatch).not.toContain("later-main.txt"); }), ); diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index 1a872fc338..bec2aa39eb 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -2282,13 +2282,15 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* const readRangeContext: GitVcsDriver.GitVcsDriver["Service"]["readRangeContext"] = Effect.fn( "readRangeContext", )(function* (cwd, baseRef) { - const range = `${baseRef}..HEAD`; + const commitRange = `${baseRef}..HEAD`; + // PR diffs start at the common ancestor when the base branch has advanced. + const diffRange = `${baseRef}...HEAD`; const [commitSummary, diffSummary, diffPatch] = yield* Effect.all( [ runGitStdoutWithOptions( "GitVcsDriver.readRangeContext.log", cwd, - ["log", "--oneline", range], + ["log", "--oneline", commitRange], { maxOutputBytes: RANGE_COMMIT_SUMMARY_MAX_OUTPUT_BYTES, appendTruncationMarker: true, @@ -2297,7 +2299,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* runGitStdoutWithOptions( "GitVcsDriver.readRangeContext.diffStat", cwd, - ["diff", "--stat", range], + ["diff", "--stat", diffRange], { maxOutputBytes: RANGE_DIFF_SUMMARY_MAX_OUTPUT_BYTES, appendTruncationMarker: true, @@ -2306,7 +2308,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* runGitStdoutWithOptions( "GitVcsDriver.readRangeContext.diffPatch", cwd, - ["diff", "--no-ext-diff", "--patch", "--minimal", range], + ["diff", "--no-ext-diff", "--patch", "--minimal", diffRange], { maxOutputBytes: RANGE_DIFF_PATCH_MAX_OUTPUT_BYTES, appendTruncationMarker: true, From dbc8eeb130b8d3b772f1b4cce0df801a6b4588d7 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 11:01:11 -0600 Subject: [PATCH 028/104] feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh --- .agents/upstream-review.md | 1 + .../src/app/DesktopObservability.test.ts | 64 +++++++++++ apps/desktop/src/app/DesktopObservability.ts | 14 ++- .../DesktopBackendConfiguration.test.ts | 43 +++++++ .../backend/DesktopBackendConfiguration.ts | 3 + apps/server/src/bin.test.ts | 2 + apps/server/src/cli/config.test.ts | 107 +++++++++++++++++- apps/server/src/cli/config.ts | 33 ++++-- apps/server/src/cli/pair.ts | 2 + apps/server/src/config.ts | 3 + .../src/environment/ServerEnvironment.test.ts | 2 + .../src/observability/Layers/Observability.ts | 12 +- apps/server/src/server.test.ts | 2 + apps/server/src/serverLogger.test.ts | 2 + docs/operations/observability.md | 12 ++ packages/shared/package.json | 4 + packages/shared/src/otelEnvironment.test.ts | 82 ++++++++++++++ packages/shared/src/otelEnvironment.ts | 105 +++++++++++++++++ 18 files changed, 478 insertions(+), 15 deletions(-) create mode 100644 packages/shared/src/otelEnvironment.test.ts create mode 100644 packages/shared/src/otelEnvironment.ts diff --git a/.agents/upstream-review.md b/.agents/upstream-review.md index 00bfa6b5f3..0a3b36ea40 100644 --- a/.agents/upstream-review.md +++ b/.agents/upstream-review.md @@ -37,6 +37,7 @@ Historical groups are indexed in the linked archive. This file migration changes | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Explicit provider refresh cache bypass / `f193a6863c494fdfa36a1ef3132e42a7d7b42926` | `f193a6863c494fdfa36a1ef3132e42a7d7b42926` (#13109) | Implemented in PR #743, pending merge: explicit refresh bypasses the model manifest freshness/retry timer and provider-owned capability, model and package-version caches before status probes. Background probes retain their timers; Pylon's installed-provider `refreshModels` path remains. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #743](https://github.com/pylon-code/pylon/pull/743); independent adversarial review, 246 focused tests, server typecheck and scoped lint/format. | | Background GitHub PR quota / `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` | `5975ec78b7eaff654e6183b99f8de7a05591e99d` (#13189), `18de6bb328059703b5d1b0661610076e3c0fa2d4` (#13198), `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` (#13200) | Adapted all three: skip uncached recheck when a terminal link cannot settle, batch background PR summaries, and avoid owner-qualified head probes. Preserve Pylon credential scopes, GraphQL budget and per-environment routing; exclude upstream-only test fixture APIs. No cursor advance. | [GitHub quota #749](https://github.com/pylon-code/pylon/pull/749); focused server regressions, scoped lint and typecheck. | +| OTLP export kill switch / `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` | `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` (#13355) | Implemented in PR #746, pending merge: `OTEL_SDK_DISABLED` and the inherited `T3CODE_OTEL_SDK_DISABLED` override suppress server and desktop main-process OTLP traces, metrics, and logs from environment, bootstrap, or Settings endpoints; WSL inherits the flags. Local trace files and local logs remain active, and Pylon service identity is retained. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #746](https://github.com/pylon-code/pylon/pull/746); independent adversarial review, 80 focused tests, server/desktop/shared typechecks and scoped lint/format. | | OTLP log export, desktop main process telemetry, and per-signal observability / `fa1e1715538f6f8ac076761f7821f68479f616ab` | `3bb06ad910389fcfc886bd5156f41dc73f307f80` (#12493), `82059df1523d070b00ab6f45159115f626a4977e` (#12520), `a8693eb4ed3a66d72e6b814117e7beeaf0fb5f8b` (#12540), `fa1e1715538f6f8ac076761f7821f68479f616ab` (#12657) | Adopted all four in Cycle #689 (PR #690). Server exports structured log records over OTLP with trace/span ID correlation, replaces `tracerLogger` to eliminate duplicate in-span span-event exports, and applies dedicated flusher/serialization layers. Desktop exports main process traces and logs over OTLP while withholding metric emissions until metrics are instrumented. OTLP settings and HTTP header sets are isolated per signal (traces, metrics, logs) across server and desktop configurations. Adversarial review findings resolved: preserved `pylon-server` runtime and service identity defaults, verified `DEFAULT_SIGNAL_EXPORT` fallback, tested backward compatibility of `ServerObservability` schema decoding, and validated log deduplication. Cursor unchanged. | Cycle [#689](https://github.com/pylon-code/pylon/issues/689), [PR #690](https://github.com/pylon-code/pylon/pull/690); independent adversarial review, new ServerLogger test suite, 9 desktop observability tests, all CI checks green. | | Web chat/diff polish, editor discovery, diff cache eviction, ACP elicitation, and preview recovery / `c14f6015bfe479d313355cb234af1a5c16dbb15f` | `c14f6015bfe479d313355cb234af1a5c16dbb15f` (#12453), `0ff87f251dafb32703d5f531e7b248ad0a581ee2` (#10831), `4a560b4e4ebb37efb7f57805ba79e37f5500bdca` (#12438), `9accc567670da5096e9a694c1adb0d35adb51d9d` (#12571), `dfbb11bdd7c3f1a5575cb55d3e3abb12be025727` (#12377), `408ff8ae9bd7eb2e7e90cbfd8b3fcfe63641bf23` (#12439), `cb3d95c17487f3d834a5537f7c3aa0106bc9b5ec` (#12523), `de6a230db0bb71d3fa91d8de0ae7067b68253f82` (#11294), `efb96939fbb135b9f4de3bff19447200b178b23a` (#7861), `5378f87f99175bded2b9241876319567875aca03` (#12535) | Adopted all ten across Cycle #685 Group A (PR #686) and Group B (PR #687). Retained whitespace when copying diff contents, kept linebreaks in citation content, opened chat popovers with relative placement, discovered installed external editors on desktop outside PATH, wrapped long titles in confirmation dialogs, showed plain text in collapsed thought previews, released oversized pull request diff cache entries, accepted ACP SDK elicitation requests (`session/elicitation` & `elicitation/create`), surfaced Codex app permission requests as approvable, and recovered preview host registration after request timeouts via connection queue eviction. Hardening and adversarial review findings resolved: normalized `acceptAlways` approval decision in Codex session runtime to grant permissions, enforced retirement epoch and quarantine boundaries on approval requests, scoped event emission with `CodexNotificationEpoch`, wrapped diff cache invalidation in `Effect.uninterruptible`, cleaned up mobile icon maps, and rebranded pre-existing PR tool descriptions to Pylon. Preserved Pylon Effect services, session lifecycles, and UI layout authority. Cursor unchanged. | Cycle [#685](https://github.com/pylon-code/pylon/issues/685), [PR #686](https://github.com/pylon-code/pylon/pull/686) and [PR #687](https://github.com/pylon-code/pylon/pull/687); independent adversarial reviews, 260+ focused unit tests across web, server, and effect-acp, full workspace typecheck, scoped format/lint. | | Web interaction, ACP session startup stderr, Claude continuation, and GTK4 snapshot / `52d08a14b9e475371658fabbb12c056c3d3dad67` | `8dd02470b1e7603b8d36a21ae27c510480351f18` (#12552), `7445aa733ada33e45289e5aa5055f79142556513` (#11263), `599c9776eb887d4fff42da7d2daf8aa07ee9a9e2` (#11268), `dcf8942304cfef382af55c0068cb2f04c86757fc` (#12636), `e36725682bd2935f1bcfdf81660cb569b5f3df29` (#12577), `63ff33756c47f127391962458d8388a647e8ed44` (#12624), `d6f291303ddc0c9a14f570266a4d9eff6d431593` (#12625), `52d08a14b9e475371658fabbb12c056c3d3dad67` (#12635) | Adopted all eight across Cycle #682 Group A (PR #683) and Group B (PR #684). Preserved draft question input when clicking options, desktop CSP screenshot annotations, restored providers settings heading, aligned PR detail menu glyphs. Empty Claude homePath correctly resolves and shares continuation with `~/.claude` or `CLAUDE_CONFIG_DIR`, surfaced ACP stderr excerpt in session startup failures instead of generic closed session errors, and extracted desktop accessible text for Flatpak and GTK4 apps in SnapShot. Adversarial review findings resolved: ACP stderr excerpt sanitized against tokens/credentials and pending buffer bounded; defensive empty `homePath` trimming and `CLAUDE_CONFIG_DIR` resolution in OAuth usage; desktop accessibility timeout preserved without premature concurrency overlap. Preserved Pylon provider rate limits and error reporting. Cursor unchanged. | Cycle [#682](https://github.com/pylon-code/pylon/issues/682), [PR #683](https://github.com/pylon-code/pylon/pull/683) and [PR #684](https://github.com/pylon-code/pylon/pull/684); independent adversarial reviews, 225+ focused tests across desktop and server, full workspace typecheck, scoped format/lint. | diff --git a/apps/desktop/src/app/DesktopObservability.test.ts b/apps/desktop/src/app/DesktopObservability.test.ts index d7ccfc43b1..215998631e 100644 --- a/apps/desktop/src/app/DesktopObservability.test.ts +++ b/apps/desktop/src/app/DesktopObservability.test.ts @@ -1,6 +1,7 @@ import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, describe, it } from "@effect/vitest"; +import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; @@ -427,6 +428,36 @@ describe("DesktopObservability", () => { ); }); + it.effect("exports kill switch warnings through the configured logger", () => { + const requests: Array = []; + return Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const baseDir = yield* fileSystem.makeTempDirectoryScoped({ + prefix: "t3-desktop-observability-test-", + }); + const environmentLayer = makeEnvironmentLayer(baseDir, true, { + T3CODE_OTLP_LOGS_URL: "https://collector.example.com/v1/logs", + }); + + yield* Effect.scoped( + Effect.void.pipe( + Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), + ), + ); + + assert.include(requests[0]?.body ?? "", "OTEL_SDK_DISABLED=1 was read as false"); + }).pipe( + Effect.scoped, + Effect.provide( + Layer.mergeAll( + NodeServices.layer, + collectorLayer(requests), + ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "1" } })), + ), + ), + ); + }); + it.effect("reads every signal endpoint from Settings when the environment names none", () => { const requests: Array = []; return Effect.gen(function* () { @@ -491,4 +522,37 @@ describe("DesktopObservability", () => { Effect.provide(Layer.mergeAll(NodeServices.layer, collectorLayer(requests))), ); }); + + it.effect("stops every export when the OpenTelemetry SDK is disabled", () => { + const requests: Array = []; + return Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const baseDir = yield* fileSystem.makeTempDirectoryScoped({ + prefix: "t3-desktop-observability-test-", + }); + const environmentLayer = makeEnvironmentLayer(baseDir); + yield* writeObservabilitySettings(environmentLayer, { + otlpTracesUrl: "https://settings.example.com/v1/traces", + otlpLogsUrl: "https://settings.example.com/v1/logs", + }); + + yield* Effect.scoped( + Effect.logInfo("desktop log stays local when disabled").pipe( + Effect.withSpan("desktop-disabled-test"), + Effect.provide(DesktopObservability.layer.pipe(Layer.provideMerge(environmentLayer))), + ), + ); + + assert.lengthOf(requests, 0); + }).pipe( + Effect.scoped, + Effect.provide( + Layer.mergeAll( + NodeServices.layer, + collectorLayer(requests), + ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })), + ), + ), + ); + }); }); diff --git a/apps/desktop/src/app/DesktopObservability.ts b/apps/desktop/src/app/DesktopObservability.ts index 9665721574..f8fd73a636 100644 --- a/apps/desktop/src/app/DesktopObservability.ts +++ b/apps/desktop/src/app/DesktopObservability.ts @@ -4,6 +4,7 @@ import { makeTraceSink, otlpSerializationLayer, } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import { parsePersistedServerObservabilitySettings, type PersistedServerObservabilitySettings, @@ -353,12 +354,18 @@ const readPersistedObservabilitySettings: Effect.Effect< * resolve traces against one revision of the file and logs against another. */ const resolveOtlpEndpoints = Effect.gen(function* () { + const otel = yield* OtelEnvironment.load; + if (otel.disabled) { + return { traces: undefined, metrics: undefined, logs: undefined, warnings: otel.warnings }; + } + const environment = yield* DesktopEnvironment.DesktopEnvironment; const persisted = yield* readPersistedObservabilitySettings; return { traces: Option.getOrUndefined(environment.otlpTracesUrl) ?? persisted.otlpTracesUrl, metrics: Option.getOrUndefined(environment.otlpMetricsUrl) ?? persisted.otlpMetricsUrl, logs: Option.getOrUndefined(environment.otlpLogsUrl) ?? persisted.otlpLogsUrl, + warnings: otel.warnings, }; }); @@ -671,7 +678,12 @@ const telemetryLayer = Layer.unwrap( // resource, // }).pipe(Layer.provide(serializationLayer)); - return Layer.mergeAll(loggerLayer, tracerLayer); + // Logged once the loggers above are installed, so the warnings use them. + const otelWarningsLayer = Layer.effectDiscard( + Effect.forEach(endpoints.warnings, (warning) => Effect.logWarning(warning)), + ); + + return otelWarningsLayer.pipe(Layer.provideMerge(Layer.mergeAll(loggerLayer, tracerLayer))); }), ); diff --git a/apps/desktop/src/backend/DesktopBackendConfiguration.test.ts b/apps/desktop/src/backend/DesktopBackendConfiguration.test.ts index 62a11381fe..de63e91b10 100644 --- a/apps/desktop/src/backend/DesktopBackendConfiguration.test.ts +++ b/apps/desktop/src/backend/DesktopBackendConfiguration.test.ts @@ -1055,6 +1055,49 @@ describe("DesktopBackendConfiguration", () => { }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), ); + it.effect("resolveWsl carries the kill switch into the distro", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const baseDir = yield* fileSystem.makeTempDirectoryScoped({ + prefix: "t3-desktop-backend-config-test-", + }); + + const previousWslEnv = process.env.WSLENV; + const previousDisabled = process.env.OTEL_SDK_DISABLED; + try { + delete process.env.WSLENV; + process.env.OTEL_SDK_DISABLED = "true"; + + yield* Effect.gen(function* () { + const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration; + const config = yield* configuration.resolveWsl({ port: 5050, distro: null }); + + assert.equal(config.env.OTEL_SDK_DISABLED, "true"); + assert.include((config.env.WSLENV ?? "").split(":"), "OTEL_SDK_DISABLED"); + }).pipe( + Effect.provide( + DesktopBackendConfiguration.layer.pipe( + Layer.provideMerge(serverExposureLayer), + Layer.provideMerge(DesktopAppSettings.layerTest()), + Layer.provideMerge(DesktopWslServerTree.layerTest()), + Layer.provideMerge( + DesktopWslEnvironment.layerTest({ + isAvailable: true, + windowsToWslPath: () => Option.some("/mnt/c/repo/apps/server/src/index.ts"), + getDistroIp: () => Option.some("172.27.0.99"), + }), + ), + Layer.provideMerge(makeEnvironmentLayer(baseDir, { platform: "win32" })), + ), + ), + ); + } finally { + restoreEnv("WSLENV", previousWslEnv); + restoreEnv("OTEL_SDK_DISABLED", previousDisabled); + } + }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)), + ); + it.effect("resolveWsl preserves existing WSLENV entries when forwarding backend secrets", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; diff --git a/apps/desktop/src/backend/DesktopBackendConfiguration.ts b/apps/desktop/src/backend/DesktopBackendConfiguration.ts index 711405002f..8df5f799e1 100644 --- a/apps/desktop/src/backend/DesktopBackendConfiguration.ts +++ b/apps/desktop/src/backend/DesktopBackendConfiguration.ts @@ -96,6 +96,9 @@ const DESKTOP_BACKEND_ENV_NAMES = [ const WSL_FORWARDED_ENV_NAMES = [ "OPENAI_API_KEY", "ANTHROPIC_API_KEY", + // Otherwise the WSL server keeps exporting to endpoints from the bootstrap. + "T3CODE_OTEL_SDK_DISABLED", + "OTEL_SDK_DISABLED", "T3CODE_OTLP_HEADERS", "T3CODE_OTLP_PROTOCOL", ] as const; diff --git a/apps/server/src/bin.test.ts b/apps/server/src/bin.test.ts index 42fbb7815c..d6163bb463 100644 --- a/apps/server/src/bin.test.ts +++ b/apps/server/src/bin.test.ts @@ -16,6 +16,7 @@ import { import * as NetService from "@t3tools/shared/Net"; import { HostProcessEnvironment } from "@t3tools/shared/hostProcess"; import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import { assert, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as DateTime from "effect/DateTime"; @@ -106,6 +107,7 @@ const makeCliTestServerConfig = (baseDir: string) => otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "t3-server", + otelEnvironment: OtelEnvironment.none, mode: "web", port: 0, host: "127.0.0.1", diff --git a/apps/server/src/cli/config.test.ts b/apps/server/src/cli/config.test.ts index b15787efeb..20fcf45891 100644 --- a/apps/server/src/cli/config.test.ts +++ b/apps/server/src/cli/config.test.ts @@ -18,6 +18,7 @@ import { } from "@t3tools/contracts"; import * as NetService from "@t3tools/shared/Net"; import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import * as NodeServices from "@effect/platform-node/NodeServices"; import * as TestConsole from "effect/testing/TestConsole"; import { Command } from "effect/unstable/cli"; @@ -58,7 +59,8 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { otlpTracesExport: DEFAULT_SIGNAL_EXPORT, otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, - otlpServiceName: "t3-server", + otlpServiceName: "pylon-server", + otelEnvironment: OtelEnvironment.none, devAllowedOrigins: [], } as const; @@ -582,6 +584,109 @@ it.layer(NodeServices.layer)("cli config resolution", (it) => { }), ); + it.effect("zeroes an endpoint stored in Settings when the SDK is disabled", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-otel-off-" }); + const derivedPaths = yield* deriveExplicitServerPaths(baseDir, undefined); + yield* fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true }); + yield* fs.writeFileString( + derivedPaths.settingsPath, + // @effect-diagnostics-next-line preferSchemaOverJson:off + `${JSON.stringify({ + observability: { + otlpTracesUrl: "http://localhost:4318/v1/traces", + otlpMetricsUrl: "http://localhost:4318/v1/metrics", + otlpLogsUrl: "http://localhost:4318/v1/logs", + }, + })}\n`, + ); + + const resolved = yield* resolveServerConfig( + { + mode: Option.some("desktop"), + port: Option.some(4888), + host: Option.none(), + baseDir: Option.some(baseDir), + cwd: Option.none(), + devUrl: Option.none(), + noBrowser: Option.none(), + bootstrapFd: Option.none(), + autoBootstrapProjectFromCwd: Option.none(), + logWebSocketEvents: Option.none(), + tailscaleServeEnabled: Option.none(), + tailscaleServePort: Option.none(), + }, + Option.none(), + ).pipe( + Effect.provide( + Layer.mergeAll( + ConfigProvider.layer(ConfigProvider.fromEnv({ env: { OTEL_SDK_DISABLED: "true" } })), + NetService.layer, + ), + ), + ); + + // The switch beats every source, including an endpoint stored in Settings. + expect(resolved.otlpTracesUrl).toBeUndefined(); + expect(resolved.otlpMetricsUrl).toBeUndefined(); + expect(resolved.otlpLogsUrl).toBeUndefined(); + expect(resolved.otelEnvironment.disabled).toBe(true); + }), + ); + + it.effect("lets T3CODE_OTEL_SDK_DISABLED=false override an ambient OTEL_SDK_DISABLED=true", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-cli-config-otel-on-" }); + const derivedPaths = yield* deriveExplicitServerPaths(baseDir, undefined); + yield* fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true }); + yield* fs.writeFileString( + derivedPaths.settingsPath, + // @effect-diagnostics-next-line preferSchemaOverJson:off + `${JSON.stringify({ + observability: { + otlpTracesUrl: "http://localhost:4318/v1/traces", + }, + })}\n`, + ); + + const resolved = yield* resolveServerConfig( + { + mode: Option.some("desktop"), + port: Option.some(4888), + host: Option.none(), + baseDir: Option.some(baseDir), + cwd: Option.none(), + devUrl: Option.none(), + noBrowser: Option.none(), + bootstrapFd: Option.none(), + autoBootstrapProjectFromCwd: Option.none(), + logWebSocketEvents: Option.none(), + tailscaleServeEnabled: Option.none(), + tailscaleServePort: Option.none(), + }, + Option.none(), + ).pipe( + Effect.provide( + Layer.mergeAll( + ConfigProvider.layer( + ConfigProvider.fromEnv({ + env: { T3CODE_OTEL_SDK_DISABLED: "false", OTEL_SDK_DISABLED: "true" }, + }), + ), + NetService.layer, + ), + ), + ); + + expect(resolved.otelEnvironment.disabled).toBe(false); + expect(resolved.otlpTracesUrl).toBe("http://localhost:4318/v1/traces"); + }), + ); + it.effect("forces noBrowser and disables auto-bootstrap for headless startup presentation", () => Effect.gen(function* () { const { join } = yield* Path.Path; diff --git a/apps/server/src/cli/config.ts b/apps/server/src/cli/config.ts index c876450505..3e7d685492 100644 --- a/apps/server/src/cli/config.ts +++ b/apps/server/src/cli/config.ts @@ -4,6 +4,7 @@ import { OtlpProtocol, type SignalExport, } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import { parsePersistedServerObservabilitySettings } from "@t3tools/shared/serverSettings"; import { DesktopBackendBootstrap, PortSchema } from "@t3tools/contracts"; import * as Config from "effect/Config"; @@ -106,7 +107,9 @@ const EnvServerConfig = Config.all({ otlpExportIntervalMs: Config.int("T3CODE_OTLP_EXPORT_INTERVAL_MS").pipe( Config.withDefault(10_000), ), - otlpServiceName: Config.string("T3CODE_OTLP_SERVICE_NAME").pipe(Config.withDefault("t3-server")), + otlpServiceName: Config.string("T3CODE_OTLP_SERVICE_NAME").pipe( + Config.withDefault("pylon-server"), + ), otlpHeaders: Config.schema(OtlpHeadersFromString, "T3CODE_OTLP_HEADERS").pipe( Config.option, Config.map(Option.getOrUndefined), @@ -372,7 +375,9 @@ export const resolveServerConfig = ( ); const logLevel = Option.getOrElse(cliLogLevel, () => env.logLevel); - // Pylon's own OTLP variables name no signal, so the one answer they give + const otel = yield* OtelEnvironment.load; + + // Pylon's inherited OTLP variables name no signal, so the one answer they give // is the answer for all three. const signalExport: SignalExport = { protocol: env.otlpProtocol, @@ -387,20 +392,24 @@ export const resolveServerConfig = ( traceBatchWindowMs: env.traceBatchWindowMs, traceMaxBytes: env.traceMaxBytes, traceMaxFiles: env.traceMaxFiles, - otlpTracesUrl: - env.otlpTracesUrl ?? - bootstrap?.otlpTracesUrl ?? - persistedObservabilitySettings.otlpTracesUrl, - otlpMetricsUrl: - env.otlpMetricsUrl ?? - bootstrap?.otlpMetricsUrl ?? - persistedObservabilitySettings.otlpMetricsUrl, - otlpLogsUrl: - env.otlpLogsUrl ?? bootstrap?.otlpLogsUrl ?? persistedObservabilitySettings.otlpLogsUrl, + otlpTracesUrl: otel.disabled + ? undefined + : (env.otlpTracesUrl ?? + bootstrap?.otlpTracesUrl ?? + persistedObservabilitySettings.otlpTracesUrl), + otlpMetricsUrl: otel.disabled + ? undefined + : (env.otlpMetricsUrl ?? + bootstrap?.otlpMetricsUrl ?? + persistedObservabilitySettings.otlpMetricsUrl), + otlpLogsUrl: otel.disabled + ? undefined + : (env.otlpLogsUrl ?? bootstrap?.otlpLogsUrl ?? persistedObservabilitySettings.otlpLogsUrl), otlpTracesExport: signalExport, otlpMetricsExport: signalExport, otlpLogsExport: signalExport, otlpServiceName: env.otlpServiceName, + otelEnvironment: otel, mode, port, cwd, diff --git a/apps/server/src/cli/pair.ts b/apps/server/src/cli/pair.ts index 801d877c9c..6fc0da6a5c 100644 --- a/apps/server/src/cli/pair.ts +++ b/apps/server/src/cli/pair.ts @@ -16,6 +16,7 @@ import { } from "@t3tools/contracts"; import { resolveWorktreeT3Home } from "@t3tools/shared/devHome"; import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import { buildTailscaleHttpsBaseUrl, DEFAULT_TAILSCALE_SERVE_PORT, @@ -326,6 +327,7 @@ const makePairServerConfig = Effect.fn(function* (input: { otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "pylon-server", + otelEnvironment: OtelEnvironment.none, mode: "web", port: state.port, host: state.host, diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index 573d61432e..b6738325ac 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -17,6 +17,7 @@ import * as Schema from "effect/Schema"; import { sweepStalePendingAttachments } from "./attachmentStore.ts"; import { DEFAULT_SIGNAL_EXPORT, type SignalExport } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; export const DEFAULT_PORT = 3773; @@ -81,6 +82,7 @@ export class ServerConfig extends Context.Service< readonly otlpMetricsExport: SignalExport; readonly otlpLogsExport: SignalExport; readonly otlpServiceName: string; + readonly otelEnvironment: OtelEnvironment.OtelEnvironment; readonly mode: RuntimeMode; readonly port: number; readonly host: string | undefined; @@ -215,6 +217,7 @@ const makeTest = Effect.fn("ServerConfig.makeTest")(function* ( otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "pylon-server", + otelEnvironment: OtelEnvironment.none, cwd, baseDir, ...derivedPaths, diff --git a/apps/server/src/environment/ServerEnvironment.test.ts b/apps/server/src/environment/ServerEnvironment.test.ts index a1b9493b13..71f228b3dc 100644 --- a/apps/server/src/environment/ServerEnvironment.test.ts +++ b/apps/server/src/environment/ServerEnvironment.test.ts @@ -11,6 +11,7 @@ import * as PlatformError from "effect/PlatformError"; import * as Schema from "effect/Schema"; import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import { @@ -60,6 +61,7 @@ const makeServerConfig = Effect.fn(function* (baseDir: string) { otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "t3-server", + otelEnvironment: OtelEnvironment.none, cwd: process.cwd(), baseDir, mode: "web", diff --git a/apps/server/src/observability/Layers/Observability.ts b/apps/server/src/observability/Layers/Observability.ts index 8f7b607745..a754933e5d 100644 --- a/apps/server/src/observability/Layers/Observability.ts +++ b/apps/server/src/observability/Layers/Observability.ts @@ -20,6 +20,7 @@ import * as BrowserTraceCollector from "../BrowserTraceCollector.ts"; export const ObservabilityLive = Layer.unwrap( Effect.gen(function* () { const config = yield* ServerConfig.ServerConfig; + const traces = config.otlpTracesExport; const metrics = config.otlpMetricsExport; // The trace serializer stays in the returned context because the browser @@ -86,6 +87,15 @@ export const ObservabilityLive = Layer.unwrap( resource, }).pipe(Layer.provide(otlpSerializationLayer(metrics.protocol))); - return Layer.mergeAll(ServerLoggerLive, traceReferencesLayer, tracerLayer, metricsLayer); + // Logged once the server's loggers are installed, so the warnings use them. + const otelWarningsLayer = Layer.effectDiscard( + Effect.forEach(config.otelEnvironment.warnings, (warning) => Effect.logWarning(warning)), + ); + + return otelWarningsLayer.pipe( + Layer.provideMerge( + Layer.mergeAll(ServerLoggerLive, traceReferencesLayer, tracerLayer, metricsLayer), + ), + ); }), ); diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index 75ba2db39f..04c71a54ac 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -231,6 +231,7 @@ import { } from "../integration/TransferBudgetReport.integration.ts"; import { symlinksSupported } from "@t3tools/shared/testing/symlinks"; import { DEFAULT_SIGNAL_EXPORT, otlpSerializationLayer } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; const defaultProjectId = ProjectId.make("project-default"); const defaultThreadId = ThreadId.make("thread-default"); @@ -576,6 +577,7 @@ const buildAppUnderTest = (options?: { otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "t3-server", + otelEnvironment: OtelEnvironment.none, mode: "desktop", port: 0, host: "127.0.0.1", diff --git a/apps/server/src/serverLogger.test.ts b/apps/server/src/serverLogger.test.ts index d6abdfd197..c259526a2a 100644 --- a/apps/server/src/serverLogger.test.ts +++ b/apps/server/src/serverLogger.test.ts @@ -9,6 +9,7 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import { DEFAULT_SIGNAL_EXPORT } from "@t3tools/shared/observability"; +import * as OtelEnvironment from "@t3tools/shared/otelEnvironment"; import * as ServerConfig from "./config.ts"; import { ServerLoggerLive } from "./serverLogger.ts"; @@ -57,6 +58,7 @@ const configLayer = (overrides: Partial) = otlpMetricsExport: DEFAULT_SIGNAL_EXPORT, otlpLogsExport: DEFAULT_SIGNAL_EXPORT, otlpServiceName: "pylon-server", + otelEnvironment: OtelEnvironment.none, cwd: baseDir, baseDir, ...derivedPaths, diff --git a/docs/operations/observability.md b/docs/operations/observability.md index 807a2bd3bc..3659377718 100644 --- a/docs/operations/observability.md +++ b/docs/operations/observability.md @@ -555,6 +555,18 @@ OTLP export: If the OTLP URLs are unset, local tracing still works, metrics stay in-process only, and logs stay on stdout only. +### The Kill Switch + +`T3CODE_OTEL_SDK_DISABLED` and `OTEL_SDK_DISABLED` turn off every OTLP export in both the server and +the desktop main process, overriding any endpoint from the environment or Settings. Local trace +files and stdout logs are unaffected. + +`T3CODE_OTEL_SDK_DISABLED` wins when set, so `T3CODE_OTEL_SDK_DISABLED=false` re-enables export on a +machine that sets `OTEL_SDK_DISABLED` for everything else. It accepts the usual boolean spellings +(`true`/`false`, `yes`/`no`, `on`/`off`, `1`/`0`, `y`/`n`). `OTEL_SDK_DISABLED` follows the +OpenTelemetry specification and only `true` disables export, so `OTEL_SDK_DISABLED=1` does not. +Values are case-insensitive and trimmed. An unrecognized value is ignored with a startup warning. + ### What Is Instrumented Today Current high-value span and metric boundaries include: diff --git a/packages/shared/package.json b/packages/shared/package.json index 73bbd85c31..ee4fc811b2 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -55,6 +55,10 @@ "types": "./src/observability.ts", "import": "./src/observability.ts" }, + "./otelEnvironment": { + "types": "./src/otelEnvironment.ts", + "import": "./src/otelEnvironment.ts" + }, "./httpObservability": { "types": "./src/httpObservability.ts", "import": "./src/httpObservability.ts" diff --git a/packages/shared/src/otelEnvironment.test.ts b/packages/shared/src/otelEnvironment.test.ts new file mode 100644 index 0000000000..3b6dac4b50 --- /dev/null +++ b/packages/shared/src/otelEnvironment.test.ts @@ -0,0 +1,82 @@ +import { assert, describe, it } from "@effect/vitest"; +import * as ConfigProvider from "effect/ConfigProvider"; +import * as Effect from "effect/Effect"; + +import * as OtelEnvironment from "./otelEnvironment.ts"; + +const load = (env: Record) => + OtelEnvironment.load.pipe(Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env })))); + +const SPEC_OFF = + "OTEL_SDK_DISABLED is set, so no telemetry is exported, whatever configured it; set T3CODE_OTEL_SDK_DISABLED=false to export anyway"; +const T3_OFF = + "T3CODE_OTEL_SDK_DISABLED is set, so no telemetry is exported, whatever configured it"; +const specIgnored = (value: string) => + `OTEL_SDK_DISABLED=${value} was read as false; the OpenTelemetry specification recognizes only the string true, so use OTEL_SDK_DISABLED=true or T3CODE_OTEL_SDK_DISABLED to say it any other way`; + +describe("OtelEnvironment", () => { + it.effect.each([ + { name: "nothing set", env: {}, disabled: false, warnings: [] }, + // OTEL_SDK_DISABLED follows the specification: only `true`, case-insensitively. + { name: "spec true", env: { OTEL_SDK_DISABLED: "true" }, disabled: true, warnings: [SPEC_OFF] }, + { name: "spec True", env: { OTEL_SDK_DISABLED: "True" }, disabled: true, warnings: [SPEC_OFF] }, + { + name: "spec padded", + env: { OTEL_SDK_DISABLED: " true " }, + disabled: true, + warnings: [SPEC_OFF], + }, + { name: "spec false", env: { OTEL_SDK_DISABLED: "false" }, disabled: false, warnings: [] }, + { + name: "spec 1", + env: { OTEL_SDK_DISABLED: "1" }, + disabled: false, + warnings: [specIgnored("1")], + }, + { + name: "spec padded yes", + env: { OTEL_SDK_DISABLED: " yes " }, + disabled: false, + warnings: [specIgnored("yes")], + }, + // T3CODE_OTEL_SDK_DISABLED takes Config.Boolean's values, case-insensitively. + { name: "t3 1", env: { T3CODE_OTEL_SDK_DISABLED: "1" }, disabled: true, warnings: [T3_OFF] }, + { + name: "t3 TRUE", + env: { T3CODE_OTEL_SDK_DISABLED: "TRUE" }, + disabled: true, + warnings: [T3_OFF], + }, + { name: "t3 n", env: { T3CODE_OTEL_SDK_DISABLED: "n" }, disabled: false, warnings: [] }, + { + name: "t3 false overrides spec true", + env: { T3CODE_OTEL_SDK_DISABLED: "false", OTEL_SDK_DISABLED: "true" }, + disabled: false, + warnings: [], + }, + { + name: "blank t3 falls through", + env: { T3CODE_OTEL_SDK_DISABLED: " ", OTEL_SDK_DISABLED: "true" }, + disabled: true, + warnings: [SPEC_OFF], + }, + { + name: "unreadable t3 warns and falls through", + env: { T3CODE_OTEL_SDK_DISABLED: "maybe", OTEL_SDK_DISABLED: "true" }, + disabled: true, + warnings: ["T3CODE_OTEL_SDK_DISABLED=maybe is not a yes or a no and was ignored", SPEC_OFF], + }, + { + name: "bad spec value still warns when t3 answered", + env: { T3CODE_OTEL_SDK_DISABLED: "false", OTEL_SDK_DISABLED: "yes" }, + disabled: false, + warnings: [specIgnored("yes")], + }, + ])("$name", ({ env, disabled, warnings }) => + Effect.gen(function* () { + const resolved = yield* load(env); + assert.strictEqual(resolved.disabled, disabled); + assert.deepStrictEqual(resolved.warnings, warnings); + }), + ); +}); diff --git a/packages/shared/src/otelEnvironment.ts b/packages/shared/src/otelEnvironment.ts new file mode 100644 index 0000000000..8bbf50198c --- /dev/null +++ b/packages/shared/src/otelEnvironment.ts @@ -0,0 +1,105 @@ +/** + * otelEnvironment: the OpenTelemetry kill switch, shared by the server and the + * desktop main process so both agree on what turns export off. + * + * `T3CODE_OTEL_SDK_DISABLED` is read first, so a machine that sets + * `OTEL_SDK_DISABLED` for everything else can still opt Pylon back in. + * + * @module otelEnvironment + */ +import * as Config from "effect/Config"; +import * as Effect from "effect/Effect"; +import * as Schema from "effect/Schema"; +import * as SchemaTransformation from "effect/SchemaTransformation"; + +export interface OtelEnvironment { + /** Whether OTLP export is off, whatever endpoint is configured. */ + readonly disabled: boolean; + /** Messages for the caller to log once at startup. */ + readonly warnings: ReadonlyArray; +} + +interface Flag { + /** `undefined` when the variable is unset, blank, or unreadable. */ + readonly value: boolean | undefined; + readonly warning?: string; +} + +const TrimmedLowercase = Schema.String.pipe( + Schema.decodeTo( + Schema.String, + SchemaTransformation.trim().compose(SchemaTransformation.toLowerCase()), + ), +); + +/** + * Reads a boolean that accepts `truthy` and `falsy`, ignoring case and padding. + * Any other value is ignored with a warning rather than failing startup. + */ +const flag = ( + name: string, + truthy: ReadonlyArray, + falsy: ReadonlyArray, + invalid: (value: string) => string, +) => + Config.schema( + TrimmedLowercase.pipe(Schema.decodeTo(Schema.Literals([...truthy, ...falsy]))), + name, + ).pipe( + Config.map((value): Flag => ({ value: truthy.includes(value) })), + Config.orElse(() => + Config.string(name).pipe( + Config.map((raw): Flag => { + const value = raw.trim(); + return value === "" + ? { value: undefined } + : { value: undefined, warning: invalid(value) }; + }), + ), + ), + Config.withDefault({ value: undefined }), + ); + +// `Config.Boolean`'s literals, which effect does not export on their own. +const T3CODE_TRUE = ["true", "yes", "on", "1", "y"]; +const T3CODE_FALSE = ["false", "no", "off", "0", "n"]; + +export const load: Effect.Effect = Config.all({ + t3: flag( + "T3CODE_OTEL_SDK_DISABLED", + T3CODE_TRUE, + T3CODE_FALSE, + (value) => `T3CODE_OTEL_SDK_DISABLED=${value} is not a yes or a no and was ignored`, + ), + // The specification: a boolean it defines is true "only by the + // case-insensitive string `true`", implementations "MUST NOT" accept other + // values as true, and should warn about unrecognized ones. + spec: flag( + "OTEL_SDK_DISABLED", + ["true"], + ["false"], + (value) => + `OTEL_SDK_DISABLED=${value} was read as false; the OpenTelemetry specification recognizes only the string true, so use OTEL_SDK_DISABLED=true or T3CODE_OTEL_SDK_DISABLED to say it any other way`, + ), +}).pipe( + Effect.map(({ t3, spec }) => { + const disabled = t3.value ?? spec.value ?? false; + const warnings = [t3.warning, spec.warning].filter((warning) => warning !== undefined); + if (disabled) { + warnings.push( + t3.value + ? "T3CODE_OTEL_SDK_DISABLED is set, so no telemetry is exported, whatever configured it" + : "OTEL_SDK_DISABLED is set, so no telemetry is exported, whatever configured it; set T3CODE_OTEL_SDK_DISABLED=false to export anyway", + ); + } + return { disabled, warnings }; + }), + // Every read above falls back instead of failing, so this cannot happen. + Effect.orDie, +); + +/** An environment that asked for nothing, for tests and for the pairing CLI. */ +export const none: OtelEnvironment = { + disabled: false, + warnings: [], +}; From 961c4dc2b621d803e47367ed8439a3a317dcc958 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 11:04:05 -0600 Subject: [PATCH 029/104] fix(grok): show monitor and background shell lifecycle (#753) --- apps/server/scripts/acp-mock-agent.ts | 104 +++++++ .../src/provider/Layers/GrokAdapter.test.ts | 64 +++- .../server/src/provider/Layers/GrokAdapter.ts | 35 +++ .../provider/acp/XAiBackgroundTasks.test.ts | 274 ++++++++++++++++++ .../src/provider/acp/XAiBackgroundTasks.ts | 183 ++++++++++++ 5 files changed, 658 insertions(+), 2 deletions(-) create mode 100644 apps/server/src/provider/acp/XAiBackgroundTasks.test.ts create mode 100644 apps/server/src/provider/acp/XAiBackgroundTasks.ts diff --git a/apps/server/scripts/acp-mock-agent.ts b/apps/server/scripts/acp-mock-agent.ts index 1aea931038..651eaf302a 100644 --- a/apps/server/scripts/acp-mock-agent.ts +++ b/apps/server/scripts/acp-mock-agent.ts @@ -38,6 +38,8 @@ const emitXAiAskUserQuestionThenHang = const emitContentThenHang = process.env.T3_ACP_EMIT_CONTENT_THEN_HANG === "1"; const emitPlanThenHang = process.env.T3_ACP_EMIT_PLAN_THEN_HANG === "1"; const emitActiveToolThenHang = process.env.T3_ACP_EMIT_ACTIVE_TOOL_THEN_HANG === "1"; +const emitGrokMonitorPostTurnPoll = process.env.T3_ACP_EMIT_GROK_MONITOR_POST_TURN_POLL === "1"; +const emitGrokBackgroundTaskStarted = process.env.T3_ACP_EMIT_GROK_BACKGROUND_TASK_STARTED === "1"; const emitForeignSessionUpdates = process.env.T3_ACP_EMIT_FOREIGN_SESSION_UPDATES === "1"; const emitPrivateThoughtChunk = process.env.T3_ACP_EMIT_PRIVATE_THOUGHT_CHUNK === "1"; const assistantMessageIdMode = process.env.T3_ACP_ASSISTANT_MESSAGE_IDS; @@ -935,6 +937,108 @@ const program = Effect.gen(function* () { return yield* Effect.never; } + if (emitGrokMonitorPostTurnPoll) { + const monitorCallId = "call-monitor-1"; + const pollCallId = "call-monitor-poll-1"; + const taskId = "01a05f41-5107-7550-821e-79e8d1cd7687"; + const description = "Watch count-sheet Typst unit until done"; + writeJsonRpcNotification("session/update", { + sessionId: requestedSessionId, + update: { + sessionUpdate: "tool_call", + toolCallId: monitorCallId, + title: "monitor", + kind: "other", + status: "pending", + rawInput: { description }, + _meta: { + "x.ai/tool": { version: 1, name: "monitor", kind: "task", namespace: "grok_build" }, + }, + }, + }); + writeJsonRpcNotification("session/update", { + sessionId: requestedSessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId: monitorCallId, + status: "completed", + rawInput: { description }, + rawOutput: { + type: "Monitor", + taskId, + timeoutMs: 36_000_000, + }, + }, + }); + writeJsonRpcNotification("_x.ai/session/prompt_complete", { + sessionId: requestedSessionId, + promptId: promptIdFromRequestMeta(request) ?? "mock-xai-prompt-1", + stopReason: "end_turn", + agentResult: null, + }); + yield* Effect.sleep("120 millis"); + writeJsonRpcNotification("session/update", { + sessionId: requestedSessionId, + update: { + sessionUpdate: "tool_call", + toolCallId: pollCallId, + title: "get_command_or_subagent_output", + kind: "other", + status: "completed", + rawInput: { variant: "TaskOutput", task_ids: [taskId], timeout_ms: 0 }, + rawOutput: { + type: "TaskOutput", + Result: { + task_id: taskId, + command: `[monitor] ${description}`, + status: "completed", + exit_code: 0, + output: "Monitor finished.", + }, + }, + }, + }); + return yield* Effect.never; + } + + if (emitGrokBackgroundTaskStarted) { + const toolCallId = "call-fb9d0000-0000-0000-0000-000000000026"; + const command = "sleep 40; echo done-a"; + writeJsonRpcNotification("session/update", { + sessionId: requestedSessionId, + update: { + sessionUpdate: "tool_call", + toolCallId, + title: "run_terminal_command", + kind: "execute", + status: "in_progress", + rawInput: { command }, + }, + }); + writeJsonRpcNotification("session/update", { + sessionId: requestedSessionId, + update: { + sessionUpdate: "tool_call_update", + toolCallId, + status: "completed", + rawOutput: { + type: "BackgroundTaskStarted", + task_id: toolCallId, + task_type: "bash", + status: "running", + command, + }, + }, + }); + writeJsonRpcNotification("_x.ai/session/prompt_complete", { + sessionId: requestedSessionId, + promptId: promptIdFromRequestMeta(request) ?? "mock-xai-prompt-1", + stopReason: "end_turn", + agentResult: null, + }); + return yield* Effect.never; + } + if (emitInterleavedAssistantToolCalls) { const toolCallId = "tool-call-1"; diff --git a/apps/server/src/provider/Layers/GrokAdapter.test.ts b/apps/server/src/provider/Layers/GrokAdapter.test.ts index b14edfcc0b..e38de31156 100644 --- a/apps/server/src/provider/Layers/GrokAdapter.test.ts +++ b/apps/server/src/provider/Layers/GrokAdapter.test.ts @@ -25,8 +25,10 @@ import { TurnId, type ProviderRuntimeEvent, } from "@t3tools/contracts"; +import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { ServerConfig } from "../../config.ts"; +import { execScriptSource, writeFakeCli } from "../../testUtils/fakeCli.ts"; import { grokPromptSettlementBelongsToContext, isGrokEnterPlanModeToolCall, @@ -34,8 +36,7 @@ import { nextGrokPlanModeActive, selectGrokPermissionOptionId, } from "./GrokAdapter.ts"; -import { execScriptSource, writeFakeCli } from "../../testUtils/fakeCli.ts"; -import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; + const decodeGrokSettings = Schema.decodeSync(GrokSettings); const __dirname = NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)); @@ -251,6 +252,65 @@ it.layer(grokAdapterTestLayer)("GrokAdapterLive", (it) => { }), ); + for (const taskType of ["monitor", "shell"] as const) { + it.effect(`emits the ${taskType} background lifecycle`, () => + Effect.gen(function* () { + const threadId = ThreadId.make(`grok-background-${taskType}`); + const wrapperPath = yield* Effect.promise(() => + makeMockGrokWrapper({ + [taskType === "monitor" + ? "T3_ACP_EMIT_GROK_MONITOR_POST_TURN_POLL" + : "T3_ACP_EMIT_GROK_BACKGROUND_TASK_STARTED"]: "1", + }), + ); + const adapter = yield* makeTestAdapter(wrapperPath); + const events: ProviderRuntimeEvent[] = []; + const finished = yield* Deferred.make(); + const eventsFiber = yield* Stream.runForEach(adapter.streamEvents, (event) => + Effect.gen(function* () { + events.push(event); + if (event.type === (taskType === "monitor" ? "task.completed" : "turn.completed")) { + yield* Deferred.succeed(finished, undefined); + } + }), + ).pipe(Effect.forkChild); + yield* adapter.startSession({ threadId, cwd: process.cwd(), runtimeMode: "full-access" }); + yield* adapter.sendTurn({ threadId, input: "watch the unit" }); + yield* Deferred.await(finished).pipe(Effect.timeout("3 seconds")); + + const started = events.find((event) => event.type === "task.started"); + const taskId = + taskType === "monitor" + ? "01a05f41-5107-7550-821e-79e8d1cd7687" + : "call-fb9d0000-0000-0000-0000-000000000026"; + assert.equal(started?.payload.taskType, taskType); + assert.equal(started?.payload.taskId, taskId); + if (taskType === "monitor") { + const completed = events.find((event) => event.type === "task.completed"); + const turnEnd = events.findIndex((event) => event.type === "turn.completed"); + assert.equal(completed?.payload.status, "completed"); + assert.equal(completed?.payload.taskId, taskId); + assert.equal(completed?.turnId, undefined); + assert.isAtLeast(turnEnd, 0); + assert.isAbove( + events.findIndex((event) => event.type === "task.completed"), + turnEnd, + ); + assert.deepEqual( + events + .slice(turnEnd + 1) + .filter((event) => event.type === "item.updated" || event.type === "item.completed"), + [], + ); + } else { + assert.equal(started?.payload.description, "sleep 40; echo done-a"); + } + yield* Fiber.interrupt(eventsFiber); + yield* adapter.stopSession(threadId); + }).pipe(TestClock.withLive), + ); + } + it.effect("sends runtime context with the current model without changing saved prompts", () => Effect.gen(function* () { const threadId = ThreadId.make("grok-runtime-context"); diff --git a/apps/server/src/provider/Layers/GrokAdapter.ts b/apps/server/src/provider/Layers/GrokAdapter.ts index 1f0053da34..2a92dba3d3 100644 --- a/apps/server/src/provider/Layers/GrokAdapter.ts +++ b/apps/server/src/provider/Layers/GrokAdapter.ts @@ -68,6 +68,10 @@ import { normalizeGrokReasoningEffort, resolveGrokAcpBaseModelId, } from "../acp/GrokAcpSupport.ts"; +import { + buildGrokBackgroundTaskEvents, + type GrokBackgroundTaskRecord, +} from "../acp/XAiBackgroundTasks.ts"; import { extractGrokPlanMarkdownFromToolCallData, extractXAiAskUserQuestions, @@ -174,6 +178,10 @@ interface GrokSessionContext { currentModelId: string | undefined; currentReasoningEffort: string | undefined; stopped: boolean; + /** Live monitor and shell identities, with their originating turns. */ + readonly backgroundTasks: Map; + readonly completedBackgroundTaskIds: Map; + readonly ambiguousBackgroundTaskIds: Set; } function settlePendingApprovalsAsCancelled( @@ -1337,6 +1345,9 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte ? normalizeGrokReasoningEffort(requestedStartReasoningEffort) : currentStartReasoningEffort, stopped: false, + backgroundTasks: new Map(), + completedBackgroundTaskIds: new Map(), + ambiguousBackgroundTaskIds: new Set(), }; const nf = yield* Stream.runDrain( @@ -1359,6 +1370,30 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte } const notificationTurnId = resolveNotificationTurnId(ctx); + if ( + event._tag === "ToolCallUpdated" && + !ctx.stopped && + (notificationTurnId === undefined || + !ctx.interruptedTurnIds.has(notificationTurnId)) + ) { + for (const taskEvent of buildGrokBackgroundTaskEvents({ + tasks: ctx.backgroundTasks, + completedTaskIds: ctx.completedBackgroundTaskIds, + ambiguousTaskIds: ctx.ambiguousBackgroundTaskIds, + toolCallId: event.toolCall.toolCallId, + rawInput: event.toolCall.data.rawInput, + rawOutput: event.toolCall.data.rawOutput, + toolCallStatus: event.toolCall.status, + turnId: notificationTurnId, + })) { + yield* offerRuntimeEvent(ctx.sessionIncarnationId, { + ...taskEvent, + ...(yield* makeEventStamp()), + provider: PROVIDER, + threadId: ctx.threadId, + }); + } + } if ( notificationTurnId === undefined || ctx.interruptedTurnIds.has(notificationTurnId) diff --git a/apps/server/src/provider/acp/XAiBackgroundTasks.test.ts b/apps/server/src/provider/acp/XAiBackgroundTasks.test.ts new file mode 100644 index 0000000000..3f77ca451a --- /dev/null +++ b/apps/server/src/provider/acp/XAiBackgroundTasks.test.ts @@ -0,0 +1,274 @@ +import { describe, expect, it } from "vite-plus/test"; +import { TurnId } from "@t3tools/contracts"; + +import { + buildGrokBackgroundTaskEvents, + type GrokBackgroundTaskRecord, +} from "./XAiBackgroundTasks.ts"; + +const turnId = TurnId.make("turn-1"); +const monitor = { type: "Monitor", taskId: "monitor-1", timeoutMs: 60_000 }; +const shell = { type: "BackgroundTaskStarted", task_id: "shell-1", command: "sleep 40" }; + +function mapper() { + const tasks = new Map(); + const completedTaskIds = new Map(); + const ambiguousTaskIds = new Set(); + const update = ( + rawOutput: unknown, + overrides: Partial[0]> = {}, + ) => + buildGrokBackgroundTaskEvents({ + tasks, + completedTaskIds, + ambiguousTaskIds, + toolCallId: "call-1", + rawInput: { description: "Watch" }, + rawOutput, + toolCallStatus: "completed", + turnId, + ...overrides, + }); + return { tasks, completedTaskIds, ambiguousTaskIds, update }; +} + +describe("Grok background tasks", () => { + it.each([ + [monitor, "monitor-1", "monitor", "Watch"], + [shell, "shell-1", "shell", "sleep 40"], + ] as const)("starts and deduplicates %j", (output, id, taskType, description) => { + const { tasks, update } = mapper(); + expect(update(output)).toEqual([ + { + type: "task.started", + turnId, + payload: { taskId: id, taskType, description, title: description, toolUseId: "call-1" }, + }, + ]); + expect(update(output)).toEqual([]); + expect(tasks.size).toBe(1); + }); + + it("accepts automatic backgrounding before the tool becomes terminal", () => { + const { update } = mapper(); + expect(update(shell, { toolCallStatus: "inProgress" })[0]?.type).toBe("task.started"); + expect(update(monitor, { toolCallStatus: "inProgress" })).toEqual([]); + expect(update(monitor, { toolCallStatus: "failed" })).toEqual([]); + }); + + it.each([ + ["running", null, "task.progress", undefined], + ["pending", null, "task.progress", undefined], + ["completed", 0, "task.completed", "completed"], + ["success", 0, "task.completed", "completed"], + ["succeeded", 0, "task.completed", "completed"], + ["failed", 1, "task.completed", "failed"], + ["error", 1, "task.completed", "failed"], + ["stopped", 137, "task.completed", "stopped"], + ["killed", 137, "task.completed", "stopped"], + ["cancelled", 137, "task.completed", "stopped"], + [undefined, 0, "task.completed", "completed"], + [undefined, 1, "task.completed", "failed"], + ])("maps poll status %s / exit %s", (status, exit_code, type, expectedStatus) => { + const { tasks, update } = mapper(); + update(monitor); + const events = update({ + type: "TaskOutput", + Result: { + task_id: "monitor-1", + command: "[monitor:Watch]", + status, + exit_code, + output: "\n result\nmore", + }, + }); + expect(events).toEqual([ + { + type, + turnId, + payload: { + taskId: "monitor-1", + taskType: "monitor", + description: "Watch", + title: "Watch", + toolUseId: "call-1", + summary: "result", + ...(expectedStatus ? { status: expectedStatus } : {}), + }, + }, + ]); + expect(tasks.size).toBe(type === "task.progress" ? 1 : 0); + }); + + it.each([undefined, TurnId.make("turn-2")])( + "does not attribute old tasks to a later turn: %s", + (laterTurnId) => { + const { tasks, update } = mapper(); + update(shell); + const events = update( + { + type: "TaskOutput", + Result: { task_id: "shell-1", command: "sleep 40", status: "completed" }, + }, + { turnId: laterTurnId }, + ); + expect(events).toHaveLength(1); + expect(events[0]?.turnId).toBeUndefined(); + expect(tasks.size).toBe(0); + }, + ); + + it("starts unknown poll tasks before progress or completion and ignores subagents", () => { + const { tasks, update } = mapper(); + const events = update({ + type: "TaskOutput", + MultiResult: { + results: [ + { task_id: "shell-1", command: "sleep 40", status: "running" }, + { task_id: "monitor-1", command: "[monitor] Watch", status: "completed" }, + { task_id: "agent-1", command: "[subagent:executor] work", status: "running" }, + ], + }, + }); + expect(events.map(({ type }) => type)).toEqual([ + "task.started", + "task.progress", + "task.started", + "task.completed", + ]); + expect(events.map(({ payload }) => payload.taskType)).toEqual([ + "shell", + "shell", + "monitor", + "monitor", + ]); + expect([...tasks.keys()]).toEqual(["shell-1"]); + expect(events.map((event) => event.turnId)).toEqual([ + undefined, + undefined, + undefined, + undefined, + ]); + }); + + it("retires only successfully killed tasks, including mixed results", () => { + const { tasks, update } = mapper(); + update(shell); + update(monitor); + const result = { type: "KillTask", Result: { task_id: "shell-1", outcome: "killed" } }; + expect(update(result, { toolCallStatus: "failed" })).toEqual([]); + expect(tasks.size).toBe(2); + const events = update({ + type: "KillTask", + MultiResult: { + results: [ + result.Result, + { task_id: "monitor-1", outcome: "error" }, + { task_id: "unknown", outcome: "killed" }, + ], + }, + }); + expect(events).toEqual([ + { + type: "task.completed", + turnId, + payload: { + taskId: "shell-1", + taskType: "shell", + description: "sleep 40", + title: "sleep 40", + toolUseId: "call-1", + status: "stopped", + }, + }, + ]); + expect([...tasks.keys()]).toEqual(["monitor-1"]); + }); + + it("ignores repeated terminal polls and gives a reused provider id a new runtime identity", () => { + const { tasks, completedTaskIds, update } = mapper(); + update(shell); + const terminal = { + type: "TaskOutput", + Result: { task_id: "shell-1", command: "sleep 40", status: "completed" }, + }; + expect(update(terminal).map((event) => event.type)).toEqual(["task.completed"]); + expect(update(terminal)).toEqual([]); + expect(completedTaskIds.get("shell-1")).toBe("call-1"); + expect(update(shell, { toolCallId: "call-2", turnId: TurnId.make("turn-2") })).toMatchObject([ + { type: "task.started", payload: { taskId: "shell-1#call-2", toolUseId: "call-2" } }, + ]); + expect(tasks.get("shell-1")?.payload.taskId).toBe("shell-1#call-2"); + }); + + it("remembers terminal identities for the session even after many other completions", () => { + const { completedTaskIds, update } = mapper(); + const completed = (id: string) => ({ + type: "TaskOutput", + Result: { task_id: id, command: "echo done", status: "completed" }, + }); + update(completed("old-task")); + for (let index = 0; index < 513; index += 1) { + update(completed(`task-${index}`)); + } + expect(completedTaskIds.size).toBe(514); + expect(update(completed("old-task"))).toEqual([]); + }); + + it("fails closed when two live tool calls claim the same provider task id", () => { + const { tasks, ambiguousTaskIds, update } = mapper(); + update(shell); + expect(update(shell, { toolCallId: "call-2" })).toMatchObject([ + { type: "task.completed", payload: { taskId: "shell-1", status: "stopped" } }, + ]); + expect(ambiguousTaskIds.has("shell-1")).toBe(true); + expect(tasks.has("shell-1")).toBe(false); + expect( + update({ + type: "TaskOutput", + Result: { task_id: "shell-1", command: "sleep 40", status: "completed" }, + }), + ).toEqual([]); + }); + + it("bounds long descriptions and output lines before projecting them", () => { + const { update } = mapper(); + const [started] = update(monitor, { + rawInput: { description: "a".repeat(2_000) }, + }); + expect(started?.type).toBe("task.started"); + if (started?.type !== "task.started") throw new Error("expected task.started"); + expect(started?.payload.description).toHaveLength(200); + const [progress] = update({ + type: "TaskOutput", + Result: { + task_id: "monitor-1", + command: "[monitor] Watch", + status: "running", + output: "b".repeat(2_000), + }, + }); + expect(progress?.type).toBe("task.progress"); + if (progress?.type !== "task.progress") throw new Error("expected task.progress"); + expect(progress?.payload.summary).toHaveLength(512); + }); + + it.each([ + null, + [], + {}, + { type: "Text", text: "subagent_id: fake\ntype: executor\ndescription: fake" }, + { type: "Monitor", taskId: " " }, + { type: "BackgroundTaskStarted", task_id: "shell-1" }, + { + type: "TaskOutput", + MultiResult: { + results: [null, {}, { task_id: "task", command: "sleep 40", exit_code: Infinity }], + }, + }, + ])("ignores malformed or unrelated outputs: %j", (output) => { + const { tasks, update } = mapper(); + expect(update(output)).toEqual([]); + expect(tasks.size).toBe(0); + }); +}); diff --git a/apps/server/src/provider/acp/XAiBackgroundTasks.ts b/apps/server/src/provider/acp/XAiBackgroundTasks.ts new file mode 100644 index 0000000000..85dbd5b767 --- /dev/null +++ b/apps/server/src/provider/acp/XAiBackgroundTasks.ts @@ -0,0 +1,183 @@ +import { + RuntimeTaskId, + type ProviderRuntimeTaskStartedEvent, + type ProviderRuntimeTaskProgressEvent, + type ProviderRuntimeTaskCompletedEvent, + type TurnId, +} from "@t3tools/contracts"; + +type TaskEvent = + | Pick + | Pick + | Pick; + +export interface GrokBackgroundTaskRecord { + readonly rawTaskId: string; + readonly payload: { + readonly taskId: RuntimeTaskId; + readonly taskType: "monitor" | "shell"; + readonly description: string; + readonly title: string; + readonly toolUseId?: string; + }; + readonly turnId: TurnId | undefined; +} + +function record(value: unknown): Record { + return typeof value === "object" && value !== null && !Array.isArray(value) + ? (value as Record) + : {}; +} + +function text(value: unknown): string | undefined { + return typeof value === "string" ? value.trim() || undefined : undefined; +} + +function lifecycle(status: unknown, exitCode: unknown) { + switch (text(status)?.toLowerCase()) { + case "pending": + case "running": + return "running"; + case "completed": + case "success": + case "succeeded": + return "completed"; + case "failed": + case "error": + return "failed"; + case "stopped": + case "killed": + case "cancelled": + return "stopped"; + default: + return typeof exitCode === "number" && Number.isFinite(exitCode) + ? exitCode === 0 + ? "completed" + : "failed" + : undefined; + } +} + +/** Map Grok's discriminated tool results, including notifications after the turn ends. */ +export function buildGrokBackgroundTaskEvents(input: { + readonly tasks: Map; + readonly completedTaskIds: Map; + readonly ambiguousTaskIds: Set; + readonly toolCallId: string; + readonly rawInput: unknown; + readonly rawOutput: unknown; + readonly toolCallStatus: string | undefined; + readonly turnId?: TurnId | undefined; +}): TaskEvent[] { + const { tasks, completedTaskIds, ambiguousTaskIds, toolCallId, toolCallStatus, turnId } = input; + const output = record(input.rawOutput); + const events: TaskEvent[] = []; + if ( + toolCallStatus !== "completed" && + toolCallStatus !== "failed" && + output.type !== "BackgroundTaskStarted" + ) { + return events; + } + const attribution = (task: GrokBackgroundTaskRecord) => + task.turnId !== undefined && task.turnId === turnId ? { turnId } : {}; + const start = ( + id: string, + taskType: "monitor" | "shell", + description: string, + toolUseId?: string, + ) => { + if (ambiguousTaskIds.has(id)) return undefined; + const known = tasks.get(id); + if (known) { + if (!toolUseId || known.payload.toolUseId === toolUseId) return known; + // Polls identify only the provider ID. Once two live tool calls reuse it, + // their later results cannot be attributed safely to either activation. + tasks.delete(id); + completedTaskIds.set(id, known.payload.toolUseId); + ambiguousTaskIds.add(id); + events.push({ + type: "task.completed", + payload: { ...known.payload, status: "stopped" }, + ...attribution(known), + }); + return undefined; + } + const previousToolUseId = completedTaskIds.get(id); + if (completedTaskIds.has(id) && (!toolUseId || previousToolUseId === toolUseId)) { + return undefined; + } + const task: GrokBackgroundTaskRecord = { + rawTaskId: id, + payload: { + taskId: RuntimeTaskId.make(completedTaskIds.has(id) ? `${id}#${toolUseId}` : id), + taskType, + description: description.slice(0, 200), + title: description.slice(0, 200), + ...(toolUseId ? { toolUseId } : {}), + }, + // Polls can rediscover older tasks without establishing their originating turn. + turnId: toolUseId ? turnId : undefined, + }; + completedTaskIds.delete(id); + tasks.set(id, task); + events.push({ type: "task.started", payload: task.payload, ...attribution(task) }); + return task; + }; + const complete = ( + task: GrokBackgroundTaskRecord, + status: "completed" | "failed" | "stopped", + summary?: string, + ) => { + tasks.delete(task.rawTaskId); + completedTaskIds.delete(task.rawTaskId); + completedTaskIds.set(task.rawTaskId, task.payload.toolUseId); + events.push({ + type: "task.completed", + payload: { ...task.payload, status, ...(summary ? { summary } : {}) }, + ...attribution(task), + }); + }; + + if (output.type === "Monitor" && toolCallStatus === "completed") { + const id = text(output.taskId); + if (id) start(id, "monitor", text(record(input.rawInput).description) ?? "Monitor", toolCallId); + } else if (output.type === "BackgroundTaskStarted") { + const id = text(output.task_id) ?? text(output.taskId); + const command = text(output.command); + if (id && command) start(id, "shell", command.split("\n")[0]!.slice(0, 200), toolCallId); + } else if (output.type === "TaskOutput" || output.type === "KillTask") { + const results = record(output.MultiResult).results; + for (const value of Array.isArray(results) ? results : [output.Result]) { + const result = record(value); + const id = text(result.task_id); + if (!id) continue; + if (output.type === "KillTask") { + const task = tasks.get(id); + if (task && toolCallStatus === "completed" && result.outcome === "killed") + complete(task, "stopped"); + continue; + } + const command = text(result.command); + const status = lifecycle(result.status, result.exit_code); + if (!command || !status || command.startsWith("[subagent:")) continue; + const task = start(id, /^\[monitor[:\]]/.test(command) ? "monitor" : "shell", command); + if (!task) continue; + const summary = text(result.output) + ?.split("\n") + .find((line) => line.trim()) + ?.trim() + .slice(0, 512); + if (status === "running") { + events.push({ + type: "task.progress", + payload: { ...task.payload, ...(summary ? { summary } : {}) }, + ...attribution(task), + }); + } else { + complete(task, status, summary); + } + } + } + return events; +} From 6bd4401aeac68ff420d8ea24a48e7023b325aa23 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 11:10:46 -0600 Subject: [PATCH 030/104] fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge --- .../src/relay/AgentAwarenessRelay.test.ts | 138 ++++- apps/server/src/relay/AgentAwarenessRelay.ts | 144 +++++- .../relay/AgentAwarenessRelayStartup.test.ts | 488 ++++++++++++++++++ 3 files changed, 763 insertions(+), 7 deletions(-) create mode 100644 apps/server/src/relay/AgentAwarenessRelayStartup.test.ts diff --git a/apps/server/src/relay/AgentAwarenessRelay.test.ts b/apps/server/src/relay/AgentAwarenessRelay.test.ts index 02931d0534..2ea053a668 100644 --- a/apps/server/src/relay/AgentAwarenessRelay.test.ts +++ b/apps/server/src/relay/AgentAwarenessRelay.test.ts @@ -325,6 +325,9 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { const projectId = "project-1" as ProjectId; const activeThreadId = "thread-active" as ThreadId; const idleThreadId = "thread-idle" as ThreadId; + const oldCompletedId = "thread-old-completed" as ThreadId; + const newCompletedId = "thread-new-completed" as ThreadId; + const freshMessageId = "thread-fresh-message" as ThreadId; const baseThread = { projectId, @@ -351,6 +354,7 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { expect( AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ environmentId, + startedAt: Date.parse(now), projects: [ { id: projectId, @@ -374,6 +378,44 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { ...baseThread, id: idleThreadId, }, + { + ...baseThread, + id: oldCompletedId, + latestTurn: { + turnId: "turn-old" as TurnId, + state: "completed", + requestedAt: "2026-05-24T00:00:00.000Z", + startedAt: "2026-05-24T00:00:00.000Z", + completedAt: "2026-05-24T00:01:00.000Z", + assistantMessageId: null, + }, + }, + { + ...baseThread, + id: newCompletedId, + latestTurn: { + turnId: "turn-new" as TurnId, + state: "completed", + requestedAt: "2026-05-25T00:00:01.000Z", + startedAt: "2026-05-25T00:00:01.000Z", + completedAt: "2026-05-25T00:00:02.000Z", + assistantMessageId: null, + }, + }, + { + ...baseThread, + id: freshMessageId, + latestUserMessageAt: "2026-05-25T00:00:01.000Z", + session: { + threadId: freshMessageId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: "2026-05-25T00:00:02.000Z", + }, + }, { ...baseThread, id: "thread-missing-project" as ThreadId, @@ -389,7 +431,101 @@ describe.sequential("signRelayAgentActivityPublishProof", () => { }, ], }), - ).toEqual([activeThreadId]); + ).toEqual([activeThreadId, newCompletedId]); + expect( + AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ + environmentId, + startedAt: Date.parse(now), + pendingAdmissionThreads: new Set([newCompletedId]), + projects: [{ id: projectId, title: "Pylon" }], + threads: [ + { + ...baseThread, + id: newCompletedId, + latestTurn: { + turnId: "turn-new" as TurnId, + state: "completed", + requestedAt: "2026-05-25T00:00:01.000Z", + startedAt: "2026-05-25T00:00:01.000Z", + completedAt: "2026-05-25T00:00:02.000Z", + assistantMessageId: null, + }, + }, + ], + }), + ).toEqual([]); + // A provider running event proves work even when a no-checkpoint turn + // settles to ready without materializing latestTurn. + expect( + AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ + environmentId, + startedAt: Date.parse(now), + freshLifecycleThreads: new Map([ + [ + freshMessageId, + { + phase: "completed", + sequence: 2, + sessionStatus: "ready", + sessionUpdatedAt: "2026-05-25T00:00:02.000Z", + sessionIncarnationId: undefined, + }, + ], + ]), + projects: [{ id: projectId, title: "Pylon" }], + threads: [ + { + ...baseThread, + id: freshMessageId, + session: { + threadId: freshMessageId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: "2026-05-25T00:00:02.000Z", + }, + }, + ], + }), + ).toEqual([freshMessageId]); + // A running event may arrive before an old ready shell catches up, even + // when both states were stamped in the same millisecond. + expect( + AgentAwarenessRelay.resolveAgentAwarenessRelayActiveThreadIds({ + environmentId, + startedAt: Date.parse(now), + freshLifecycleThreads: new Map([ + [ + freshMessageId, + { + phase: "running", + sequence: 1, + sessionStatus: null, + sessionUpdatedAt: "2026-05-25T00:00:01.000Z", + sessionIncarnationId: undefined, + }, + ], + ]), + projects: [{ id: projectId, title: "Pylon" }], + threads: [ + { + ...baseThread, + id: freshMessageId, + session: { + threadId: freshMessageId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: "2026-05-25T00:00:01.000Z", + }, + }, + ], + }), + ).toEqual([]); }); it.effect("signs the activity publish JWT and rejects tampering", () => diff --git a/apps/server/src/relay/AgentAwarenessRelay.ts b/apps/server/src/relay/AgentAwarenessRelay.ts index 144d43a029..91f8bf45cf 100644 --- a/apps/server/src/relay/AgentAwarenessRelay.ts +++ b/apps/server/src/relay/AgentAwarenessRelay.ts @@ -267,8 +267,44 @@ export function resolveAgentAwarenessRelayPublishSnapshot(input: { }; } +type FreshLifecycleProof = { + readonly phase: "running" | "completed" | "failed"; + readonly sequence: number; + readonly sessionStatus: "ready" | "idle" | "error" | null; + readonly sessionUpdatedAt: string; + readonly sessionIncarnationId: string | undefined; +}; + +function terminalWorkSinceStart( + thread: OrchestrationThreadShell, + startedAt: number, + phase: "completed" | "failed", + freshLifecycle?: FreshLifecycleProof, +): boolean { + // An observed new lifecycle takes precedence over a checkpoint from the + // previous turn, which can remain in the shell during admission. + if (freshLifecycle === undefined) { + return Date.parse(thread.latestTurn?.completedAt ?? "") > startedAt; + } + if (freshLifecycle.phase === "running") return false; + // For an observed terminal transition, the projected shell must have caught + // up to that same session even when an older checkpoint is still present. + const session = thread.session; + return ( + session !== null && + ((phase === "completed" && freshLifecycle.phase === "completed") || + (phase === "failed" && freshLifecycle.phase === "failed")) && + session.status === freshLifecycle.sessionStatus && + session.sessionIncarnationId === freshLifecycle.sessionIncarnationId && + Date.parse(session.updatedAt) >= Date.parse(freshLifecycle.sessionUpdatedAt) + ); +} + export function resolveAgentAwarenessRelayActiveThreadIds(input: { readonly environmentId: EnvironmentId; + readonly startedAt: number; + readonly freshLifecycleThreads?: ReadonlyMap; + readonly pendingAdmissionThreads?: ReadonlySet; readonly projects: ReadonlyArray>; readonly threads: ReadonlyArray; }): ReadonlyArray { @@ -279,12 +315,22 @@ export function resolveAgentAwarenessRelayActiveThreadIds(input: { if (!project) { return false; } + const state = projectThreadAwareness({ + environmentId: input.environmentId, + project, + thread, + }); return ( - projectThreadAwareness({ - environmentId: input.environmentId, - project, - thread, - }) !== null + state !== null && + (state.phase !== "completed" && state.phase !== "failed" + ? true + : !input.pendingAdmissionThreads?.has(thread.id) && + terminalWorkSinceStart( + thread, + input.startedAt, + state.phase, + input.freshLifecycleThreads?.get(thread.id), + )) ); }) .map((thread) => thread.id); @@ -298,8 +344,12 @@ export const make = Effect.gen(function* () { const orchestrationEngine = yield* OrchestrationEngine.OrchestrationEngineService; const crypto = yield* Crypto.Crypto; const cloudLinkKeyPair = yield* getOrCreateEnvironmentKeyPairFromSecretStore(secrets); + const startedAt = (yield* DateTime.now).epochMilliseconds; const activeSnapshotPublishedRef = yield* Ref.make(false); const publishedStateByThreadRef = yield* Ref.make(new Map()); + const publishedNonNullThreads = new Set(); + const freshLifecycleThreads = new Map(); + const pendingAdmissionThreads = new Set(); const readSecretString = (name: string) => secrets @@ -417,6 +467,19 @@ export const make = Effect.gen(function* () { }); const publishIdentity = agentAwarenessPublishIdentity(snapshot.state); const publishedStateByThread = yield* Ref.get(publishedStateByThreadRef); + if (snapshot.state?.phase === "completed" || snapshot.state?.phase === "failed") { + // A new admission can leave the previous completed checkpoint projected + // until its running shell arrives. This fence applies even when the + // thread has an older published state. + if (pendingAdmissionThreads.has(threadId)) return; + const proof = freshLifecycleThreads.get(threadId); + if ( + (proof !== undefined || !publishedNonNullThreads.has(threadId)) && + (Option.isNone(thread) || + !terminalWorkSinceStart(thread.value, startedAt, snapshot.state.phase, proof)) + ) + return; + } if (publishedStateByThread.get(threadId) === publishIdentity) { // The projection is back at (or never left) the last published state, so // any pending deferred confirmation is moot. Leaving the deadline in @@ -444,7 +507,7 @@ export const make = Effect.gen(function* () { const requiresConfirmation = (snapshot.state === null && publishedStateByThread.get(threadId) !== agentAwarenessPublishIdentity(null)) || - (snapshot.state?.phase === "completed" && !publishedStateByThread.has(threadId)); + (snapshot.state?.phase === "completed" && !publishedNonNullThreads.has(threadId)); if (requiresConfirmation) { const nowMs = (yield* DateTime.now).epochMilliseconds; const deadline = publishConfirmDeadlines.get(threadId); @@ -498,6 +561,15 @@ export const make = Effect.gen(function* () { nextPublishedStates.set(threadId, publishIdentity); return nextPublishedStates; }); + if (snapshot.state === null) { + freshLifecycleThreads.delete(threadId); + pendingAdmissionThreads.delete(threadId); + } else { + publishedNonNullThreads.add(threadId); + if (snapshot.state.phase === "completed" || snapshot.state.phase === "failed") { + freshLifecycleThreads.delete(threadId); + } + } }); const publishThread: AgentAwarenessRelay["Service"]["publishThread"] = (threadId) => @@ -529,6 +601,9 @@ export const make = Effect.gen(function* () { const snapshot = yield* snapshotQuery.getShellSnapshot(); const activeThreadIds = resolveAgentAwarenessRelayActiveThreadIds({ environmentId, + startedAt, + freshLifecycleThreads, + pendingAdmissionThreads, projects: snapshot.projects, threads: snapshot.threads, }); @@ -610,12 +685,69 @@ export const make = Effect.gen(function* () { ); yield* forkParked( Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) => { + // This PubSub stream starts with the subscription and does not replay + // past events. A timestamp fence would discard valid events emitted + // later in the same millisecond as startup. const threadId = eventThreadId(event); if (threadId === null) { return Effect.logDebug("agent activity publishing ignored event without thread id", { eventType: event.type, }); } + if ( + (event.type === "thread.turn-start-requested" || + event.type === "thread.message-sent") && + event.metadata.historyImport !== true + ) { + freshLifecycleThreads.delete(threadId); + pendingAdmissionThreads.add(threadId); + publishConfirmDeadlines.delete(threadId); + } + if (event.type === "thread.session-set" && event.metadata.historyImport !== true) { + const session = event.payload.session; + const previous = freshLifecycleThreads.get(threadId); + if (session.status === "running") { + pendingAdmissionThreads.delete(threadId); + publishConfirmDeadlines.delete(threadId); + freshLifecycleThreads.set(threadId, { + phase: "running", + sequence: event.sequence, + sessionStatus: null, + sessionUpdatedAt: session.updatedAt, + sessionIncarnationId: session.sessionIncarnationId, + }); + } else if (session.status === "error") { + pendingAdmissionThreads.delete(threadId); + publishConfirmDeadlines.delete(threadId); + freshLifecycleThreads.set(threadId, { + phase: "failed", + sequence: event.sequence, + sessionStatus: "error", + sessionUpdatedAt: session.updatedAt, + sessionIncarnationId: session.sessionIncarnationId, + }); + } else if (session.status === "ready" || session.status === "idle") { + if ( + previous?.phase === "running" && + event.sequence > previous.sequence && + session.sessionIncarnationId === previous.sessionIncarnationId + ) { + freshLifecycleThreads.set(threadId, { + phase: "completed", + sequence: event.sequence, + sessionStatus: session.status, + sessionUpdatedAt: session.updatedAt, + sessionIncarnationId: session.sessionIncarnationId, + }); + } + } else { + // A new admission or stop invalidates an older completion proof + // even while the projector still serves the prior ready shell. + freshLifecycleThreads.delete(threadId); + pendingAdmissionThreads.add(threadId); + publishConfirmDeadlines.delete(threadId); + } + } if (!shouldPublishAgentAwarenessEvent(event)) { return Effect.logDebug( "agent activity publishing ignored event without activity changes", diff --git a/apps/server/src/relay/AgentAwarenessRelayStartup.test.ts b/apps/server/src/relay/AgentAwarenessRelayStartup.test.ts new file mode 100644 index 0000000000..95887f6342 --- /dev/null +++ b/apps/server/src/relay/AgentAwarenessRelayStartup.test.ts @@ -0,0 +1,488 @@ +import * as NodeServices from "@effect/platform-node/NodeServices"; +import type { + EnvironmentId, + ExecutionEnvironmentDescriptor, + OrchestrationEvent, + OrchestrationProjectShell, + OrchestrationShellSnapshot, + OrchestrationThreadShell, + ProjectId, + ThreadId, + TurnId, +} from "@t3tools/contracts"; +import { CommandId, ProviderInstanceId } from "@t3tools/contracts"; +import { describe, expect, it } from "@effect/vitest"; +import * as DateTime from "effect/DateTime"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Option from "effect/Option"; +import * as Queue from "effect/Queue"; +import * as Stream from "effect/Stream"; +import * as TestClock from "effect/testing/TestClock"; + +import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; +import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; +import { + OrchestrationEngineService, + type OrchestrationEngineShape, +} from "../orchestration/Services/OrchestrationEngine.ts"; +import { + ProjectionSnapshotQuery, + type ProjectionSnapshotQueryShape, +} from "../orchestration/Services/ProjectionSnapshotQuery.ts"; +import { + RELAY_ENVIRONMENT_CREDENTIAL_SECRET, + RELAY_URL_SECRET, + PUBLISH_AGENT_ACTIVITY_SECRET, +} from "../cloud/config.ts"; +import * as AgentAwarenessRelay from "./AgentAwarenessRelay.ts"; + +const encodeSecret = (value: string): Uint8Array => new TextEncoder().encode(value); + +function makeMemorySecretStore() { + const values = new Map(); + const store = { + get: ((name) => + Effect.sync(() => { + const value = values.get(name); + return value === undefined ? Option.none() : Option.some(Uint8Array.from(value)); + })) satisfies ServerSecretStore.ServerSecretStore["Service"]["get"], + set: ((name, value) => + Effect.sync(() => { + values.set(name, Uint8Array.from(value)); + })) satisfies ServerSecretStore.ServerSecretStore["Service"]["set"], + create: ((name, value) => + Effect.sync(() => { + values.set(name, Uint8Array.from(value)); + })) satisfies ServerSecretStore.ServerSecretStore["Service"]["create"], + getOrCreateRandom: ((name, bytes) => + Effect.sync(() => { + const existing = values.get(name); + if (existing) { + return existing; + } + const generated = new Uint8Array(bytes); + values.set(name, generated); + return generated; + })) satisfies ServerSecretStore.ServerSecretStore["Service"]["getOrCreateRandom"], + remove: ((name) => + Effect.sync(() => { + values.delete(name); + })) satisfies ServerSecretStore.ServerSecretStore["Service"]["remove"], + } satisfies ServerSecretStore.ServerSecretStore["Service"]; + return { + store, + setString: (name: string, value: string) => store.set(name, encodeSecret(value)), + }; +} + +describe.sequential("AgentAwarenessRelay startup", () => { + it.effect("does not alert for historical completions after startup", () => + Effect.scoped( + Effect.gen(function* () { + const secrets = makeMemorySecretStore(); + const now = yield* DateTime.now; + const old = DateTime.formatIso(DateTime.add(now, { days: -7 })); + // Live PubSub events can share the startup millisecond and are still new. + const afterStartup = DateTime.formatIso(now); + const threadId = "thread-old" as ThreadId; + const freshDoneThreadId = "thread-new-done" as ThreadId; + const invalidatedThreadId = "thread-new-admission" as ThreadId; + const projectId = "project-1" as ProjectId; + const environmentId = "env-1" as EnvironmentId; + const project = { + id: projectId, + title: "T3 Code", + workspaceRoot: "/workspace", + repositoryIdentity: null, + defaultModelSelection: null, + scripts: [], + createdAt: old, + updatedAt: old, + } satisfies OrchestrationProjectShell; + const completedTurn = { + turnId: "turn-1" as TurnId, + state: "completed", + requestedAt: old, + startedAt: old, + completedAt: old, + assistantMessageId: null, + } as const; + const completedThread = { + id: threadId, + projectId, + title: "Old task", + modelSelection: { instanceId: ProviderInstanceId.make("codex"), model: "gpt-5.4" }, + runtimeMode: "full-access", + interactionMode: "default", + branch: null, + worktreePath: null, + pullRequests: [], + latestTurn: completedTurn, + createdAt: old, + updatedAt: old, + archivedAt: null, + settledOverride: null, + settledAt: null, + session: null, + latestUserMessageAt: old, + hasPendingApprovals: false, + hasPendingUserInput: false, + hasActionableProposedPlan: false, + } satisfies OrchestrationThreadShell; + let currentThread: OrchestrationThreadShell | null = completedThread; + let resolveFreshDoneQuery: (() => void) | undefined; + const freshDoneQuery = new Promise((resolve) => { + resolveFreshDoneQuery = resolve; + }); + let watchFreshDoneQuery = false; + let invalidatedQueries = 0; + let resolveInvalidatedQueries: (() => void) | undefined; + const invalidatedQuery = new Promise((resolve) => { + resolveInvalidatedQueries = resolve; + }); + let freshDoneQueries = 0; + let resolveFreshReadyQuery: (() => void) | undefined; + const freshReadyQuery = new Promise((resolve) => { + resolveFreshReadyQuery = resolve; + }); + let publishes = 0; + const publishPaths: string[] = []; + const countFor = (id: ThreadId) => + publishPaths.filter((path) => path.includes(`/threads/${id}/`)).length; + let expectedThreadId = threadId; + let resolveNewQuery: (() => void) | undefined; + const newQuery = new Promise((resolve) => { + resolveNewQuery = resolve; + }); + let resolvePublished: (() => void) | undefined; + const published = new Promise((resolve) => { + resolvePublished = resolve; + }); + const events = yield* Queue.unbounded(); + const originalFetch = globalThis.fetch; + globalThis.fetch = ((input: Parameters[0]) => { + publishes += 1; + const path = String(input); + publishPaths.push(path); + if (path.includes(`/threads/${expectedThreadId}/`)) resolvePublished?.(); + return Promise.resolve(Response.json({ ok: true, deliveries: [] })); + }) as unknown as typeof fetch; + yield* Effect.addFinalizer(() => + Effect.sync(() => { + globalThis.fetch = originalFetch; + }), + ); + yield* secrets.setString(RELAY_URL_SECRET, "https://relay.example.test"); + yield* secrets.setString(RELAY_ENVIRONMENT_CREDENTIAL_SECRET, "relay-credential"); + yield* secrets.setString(PUBLISH_AGENT_ACTIVITY_SECRET, "true"); + + const layer = Layer.mergeAll( + Layer.succeed(ServerSecretStore.ServerSecretStore, secrets.store), + Layer.succeed(ServerEnvironment.ServerEnvironment, { + getEnvironmentId: Effect.succeed(environmentId), + getDescriptor: Effect.succeed({ + environmentId, + label: "Test Desktop", + platform: { os: "darwin", arch: "arm64" }, + serverVersion: "0.0.0-test", + capabilities: { repositoryIdentity: true }, + } satisfies ExecutionEnvironmentDescriptor), + }), + Layer.succeed(OrchestrationEngineService, { + streamDomainEvents: Stream.fromQueue(events), + } as OrchestrationEngineShape), + Layer.succeed(ProjectionSnapshotQuery, { + getShellSnapshot: () => + Effect.succeed({ + snapshotSequence: 1, + projects: [project], + threads: currentThread ? [currentThread] : [], + updatedAt: old, + } satisfies OrchestrationShellSnapshot), + getThreadShellById: (requestedThreadId: ThreadId) => + Effect.sync(() => { + if (currentThread?.session?.lastError === "new failure") resolveNewQuery?.(); + if (requestedThreadId === invalidatedThreadId) { + invalidatedQueries += 1; + if (invalidatedQueries >= 3) resolveInvalidatedQueries?.(); + } + if (watchFreshDoneQuery && currentThread?.id === freshDoneThreadId) { + freshDoneQueries += 1; + resolveFreshDoneQuery?.(); + if (freshDoneQueries >= 2) resolveFreshReadyQuery?.(); + } + return Option.fromNullishOr( + currentThread?.id === requestedThreadId ? currentThread : null, + ); + }), + getProjectShellById: () => Effect.succeed(Option.some(project)), + } as unknown as ProjectionSnapshotQueryShape), + ); + + yield* Effect.gen(function* () { + const relay = yield* AgentAwarenessRelay.AgentAwarenessRelay; + yield* relay.start(); + yield* relay.publishThread(threadId); + expect(publishes).toBe(0); + + currentThread = { + ...completedThread, + latestTurn: null, + latestUserMessageAt: DateTime.formatIso(DateTime.add(now, { seconds: 1 })), + session: { + threadId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: DateTime.formatIso(DateTime.add(now, { seconds: 2 })), + }, + }; + expect( + AgentAwarenessRelay.resolveAgentAwarenessRelayPublishSnapshot({ + environmentId, + threadId, + thread: Option.some(currentThread), + project: Option.some(project), + }).state?.phase, + ).toBe("completed"); + yield* relay.publishThread(threadId); + expect(publishes).toBe(0); + + currentThread = { + ...completedThread, + session: { + threadId, + status: "error", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: "old failure", + updatedAt: old, + }, + }; + yield* relay.publishThread(threadId); + expect(publishes).toBe(0); + + currentThread = { + ...completedThread, + latestTurn: null, + session: { + threadId, + status: "error", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: "new failure", + updatedAt: afterStartup, + }, + }; + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 1, + eventId: "event-new-failure", + commandId: CommandId.make("command-new-failure"), + aggregateKind: "thread", + aggregateId: threadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { threadId, session: currentThread.session }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Effect.promise(() => newQuery); + yield* Effect.promise(() => published); + expect(publishes).toBe(1); + + let resolveCompleted: (() => void) | undefined; + const completed = new Promise((resolve) => { + resolveCompleted = resolve; + }); + resolvePublished = resolveCompleted; + currentThread = { + ...completedThread, + latestTurn: null, + session: { + threadId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: afterStartup, + }, + }; + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 2, + eventId: "event-fast-completion", + commandId: CommandId.make("command-fast-completion"), + aggregateKind: "thread", + aggregateId: threadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { threadId, session: { ...currentThread.session, status: "running" } }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 3, + eventId: "event-fast-completion-ready", + commandId: CommandId.make("command-fast-completion-ready"), + aggregateKind: "thread", + aggregateId: threadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { threadId, session: currentThread.session }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Effect.promise(() => completed); + expect(publishes).toBe(2); + + expectedThreadId = freshDoneThreadId; + currentThread = { + ...completedThread, + id: freshDoneThreadId, + latestTurn: null, + session: { + threadId: freshDoneThreadId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: afterStartup, + }, + }; + yield* relay.publishThread(freshDoneThreadId); + expect(countFor(freshDoneThreadId)).toBe(0); + let resolveFirstDone: (() => void) | undefined; + const firstDone = new Promise((resolve) => { + resolveFirstDone = resolve; + }); + resolvePublished = resolveFirstDone; + watchFreshDoneQuery = true; + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 4, + eventId: "event-first-done", + commandId: CommandId.make("command-first-done"), + aggregateKind: "thread", + aggregateId: freshDoneThreadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { + threadId: freshDoneThreadId, + session: { ...currentThread.session, status: "running" }, + }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Effect.promise(() => freshDoneQuery); + expect(countFor(freshDoneThreadId)).toBe(0); + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 5, + eventId: "event-first-done-ready", + commandId: CommandId.make("command-first-done-ready"), + aggregateKind: "thread", + aggregateId: freshDoneThreadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { threadId: freshDoneThreadId, session: currentThread.session }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Effect.promise(() => freshReadyQuery); + yield* Effect.yieldNow; + expect(countFor(freshDoneThreadId)).toBe(0); + yield* TestClock.adjust("5 seconds"); + yield* Effect.promise(() => firstDone); + expect(countFor(freshDoneThreadId)).toBe(1); + + let resolveTombstone: (() => void) | undefined; + const tombstone = new Promise((resolve) => { + resolveTombstone = resolve; + }); + resolvePublished = resolveTombstone; + currentThread = null; + yield* relay.publishThread(freshDoneThreadId); + expect(countFor(freshDoneThreadId), publishPaths.join(", ")).toBe(1); + yield* TestClock.adjust("5 seconds"); + yield* Effect.promise(() => tombstone); + expect(countFor(freshDoneThreadId)).toBe(2); + yield* relay.publishThread(freshDoneThreadId); + expect(countFor(freshDoneThreadId)).toBe(2); + + // A new admission invalidates the prior Done proof while the shell + // still projects the previous ready state. + currentThread = { + ...completedThread, + id: invalidatedThreadId, + latestTurn: { ...completedTurn, completedAt: afterStartup }, + session: { + threadId: invalidatedThreadId, + status: "ready", + providerName: "Codex", + runtimeMode: "full-access", + activeTurnId: null, + lastError: null, + updatedAt: afterStartup, + }, + }; + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 6, + eventId: "event-admission-running", + commandId: CommandId.make("command-admission-running"), + aggregateKind: "thread", + aggregateId: invalidatedThreadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { + threadId: invalidatedThreadId, + session: { ...currentThread.session, status: "running" }, + }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 7, + eventId: "event-admission-ready", + commandId: CommandId.make("command-admission-ready"), + aggregateKind: "thread", + aggregateId: invalidatedThreadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { threadId: invalidatedThreadId, session: currentThread.session }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Queue.offer(events, { + type: "thread.session-set", + sequence: 8, + eventId: "event-new-admission-starting", + commandId: CommandId.make("command-new-admission-starting"), + aggregateKind: "thread", + aggregateId: invalidatedThreadId, + actor: { kind: "provider" }, + metadata: {}, + payload: { + threadId: invalidatedThreadId, + session: { ...currentThread.session, status: "starting" }, + }, + occurredAt: afterStartup, + } as unknown as OrchestrationEvent); + yield* Effect.promise(() => invalidatedQuery); + yield* TestClock.adjust("5 seconds"); + expect(countFor(invalidatedThreadId)).toBe(0); + }).pipe( + Effect.provide( + AgentAwarenessRelay.layer.pipe( + Layer.provide(layer), + Layer.provideMerge(NodeServices.layer), + ), + ), + ); + }), + ), + ); +}); From d576ab3cd9b97f7981fd43ebf9074c925c27f449 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 11:16:48 -0600 Subject: [PATCH 031/104] fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh --- .../src/features/usage/UsageRouteScreen.tsx | 13 ++ apps/server/src/usage/UsageService.test.ts | 16 +- apps/server/src/usage/UsageService.ts | 18 +- apps/web/src/components/usage/UsagePage.tsx | 26 ++- docs/user/usage.md | 4 +- packages/contracts/src/usage.ts | 8 +- packages/shared/src/usageMerge.test.ts | 179 ++++++++++++++++++ packages/shared/src/usageMerge.ts | 98 ++++++++-- 8 files changed, 343 insertions(+), 19 deletions(-) diff --git a/apps/mobile/src/features/usage/UsageRouteScreen.tsx b/apps/mobile/src/features/usage/UsageRouteScreen.tsx index a515f2ddd6..131ad22c3a 100644 --- a/apps/mobile/src/features/usage/UsageRouteScreen.tsx +++ b/apps/mobile/src/features/usage/UsageRouteScreen.tsx @@ -286,6 +286,19 @@ export function UsageRouteScreen() { {merged.duplicateSources.join(", ")} ) : null} + {merged.approximateEnvironments.length > 0 ? ( + + Totals may count shared usage more than once because these environments run older + servers or have directories whose filesystem identity could not be read:{" "} + {selectedEnvironments + .filter((environment) => + merged.approximateEnvironments.includes(environment.environmentId), + ) + .map((environment) => environment.label) + .join(", ")} + . + + ) : null} {isPending ? ( Scanning provider transcripts… diff --git a/apps/server/src/usage/UsageService.test.ts b/apps/server/src/usage/UsageService.test.ts index 7b0dcb44df..549d0a96a0 100644 --- a/apps/server/src/usage/UsageService.test.ts +++ b/apps/server/src/usage/UsageService.test.ts @@ -141,7 +141,7 @@ describe("UsageService", () => { await NodeFSP.mkdir(NodePath.join(claudeHome, "projects"), { recursive: true }); await NodeFSP.writeFile( NodePath.join(claudeHome, "projects", "session.jsonl"), - claudeLine(2, 7), + claudeLine(1, 5) + claudeLine(2, 7), ); await NodeFSP.mkdir(NodePath.join(codexHome, "sessions"), { recursive: true }); await NodeFSP.symlink(codexHome, alias, "junction"); @@ -243,6 +243,20 @@ describe("UsageService", () => { sources.filter((source) => source.fingerprint.provider === "codex").length, 1, ); + assert.strictEqual( + sources.reduce( + (sum, source) => + sum + + (source.buckets ?? []).reduce((total, bucket) => total + bucket.totals.outputTokens, 0), + 0, + ), + totalOutputTokens(summary), + ); + assert.isTrue( + sources.every((source) => + source.buckets?.every((bucket) => bucket.provider === source.fingerprint.provider), + ), + ); }).pipe(Effect.scoped), ); diff --git a/apps/server/src/usage/UsageService.ts b/apps/server/src/usage/UsageService.ts index 8d63a91b9d..7ad01d09c4 100644 --- a/apps/server/src/usage/UsageService.ts +++ b/apps/server/src/usage/UsageService.ts @@ -936,6 +936,18 @@ export const make = Effect.gen(function* () { // Distinct per directory. Buckets carry per-cell session counts, but a // session spans days and models, so clients total this figure instead. const sessionIds = new Set(); + // Keep attribution at the physical directory boundary. The primary + // aggregator decides scan-wide de-duplication first, so source buckets + // partition the same accepted records as the legacy flat buckets. + const sourceAggregator = new UsageAggregator({ + timeZone: input.timeZone, + sinceDay: input.sinceDay, + untilDay: input.untilDay, + resolution: input.resolution ?? "day", + ...hourlyWindow, + rates, + priceOverrides: createOverrideRateTable(settings.usagePriceOverrides), + }); for (const file of retainedFiles) { if (file.records.length === 0) { @@ -962,8 +974,9 @@ export const make = Effect.gen(function* () { } // Only sessions contributing in-window count; the mtime slack can // admit boundary files whose records fall outside the range. - if (aggregator.add(usageRecord) && record.sessionId.length > 0) { - sessionIds.add(record.sessionId); + if (aggregator.add(usageRecord)) { + sourceAggregator.add(usageRecord); + if (record.sessionId.length > 0) sessionIds.add(record.sessionId); } } } @@ -981,6 +994,7 @@ export const make = Effect.gen(function* () { : status === "failed" && scannedFiles > 0 ? "partial" : (status ?? "ok"), + buckets: sourceAggregator.finish().buckets, scannedFiles, skippedFiles, malformedRecords: malformedRecords ?? 0, diff --git a/apps/web/src/components/usage/UsagePage.tsx b/apps/web/src/components/usage/UsagePage.tsx index 89effa6b47..9ac7dab6c9 100644 --- a/apps/web/src/components/usage/UsagePage.tsx +++ b/apps/web/src/components/usage/UsagePage.tsx @@ -226,6 +226,7 @@ export function UsagePage() { isPartial={isPartial} duplicateSources={merged.duplicateSources} staleEnvironments={merged.staleEnvironments} + approximateEnvironments={merged.approximateEnvironments} /> @@ -642,16 +643,26 @@ function UsageCoverageNotice({ environments, duplicateSources, staleEnvironments, + approximateEnvironments, }: { readonly environments: readonly EnvironmentUsageStatus[]; readonly duplicateSources: readonly string[]; readonly staleEnvironments: readonly string[]; + readonly approximateEnvironments: readonly string[]; }) { const failed = environments.filter((environment) => environment.error !== null); const stale = environments.filter((environment) => staleEnvironments.includes(environment.environmentId), ); - if (failed.length === 0 && stale.length === 0 && duplicateSources.length === 0) { + const approximate = environments.filter((environment) => + approximateEnvironments.includes(environment.environmentId), + ); + if ( + failed.length === 0 && + stale.length === 0 && + duplicateSources.length === 0 && + approximate.length === 0 + ) { return null; } @@ -671,6 +682,13 @@ function UsageCoverageNotice({ {duplicateSources.join(", ")} ) : null} + {approximate.length > 0 ? ( + + Totals may include shared usage more than once because these environments run older + servers or have directories whose filesystem identity could not be read:{" "} + {approximate.map((entry) => entry.label).join(", ")}. + + ) : null}
); } @@ -685,6 +703,7 @@ function UsageEnvironmentFilter({ isPartial, duplicateSources, staleEnvironments, + approximateEnvironments, }: { readonly environments: readonly EnvironmentUsageStatus[]; readonly selectedEnvironments: readonly EnvironmentUsageStatus[]; @@ -694,6 +713,7 @@ function UsageEnvironmentFilter({ readonly isPartial: boolean; readonly duplicateSources: readonly string[]; readonly staleEnvironments: readonly string[]; + readonly approximateEnvironments: readonly string[]; }) { const [modelPricesOpen, setModelPricesOpen] = useState(false); const allSelected = selectedEnvironmentIds === null; @@ -708,7 +728,8 @@ function UsageEnvironmentFilter({ ).length; const hasIssue = selectedEnvironments.some((environment) => environment.error !== null) || - staleEnvironments.length > 0; + staleEnvironments.length > 0 || + approximateEnvironments.length > 0; return ( <> @@ -807,6 +828,7 @@ function UsageEnvironmentFilter({ environments={selectedEnvironments} duplicateSources={duplicateSources} staleEnvironments={staleEnvironments} + approximateEnvironments={approximateEnvironments} /> ) : null} diff --git a/docs/user/usage.md b/docs/user/usage.md index 3d9283cc80..a82bd83c63 100644 --- a/docs/user/usage.md +++ b/docs/user/usage.md @@ -13,7 +13,9 @@ Usage includes each configured account's history, including disabled accounts. C the account's home setting or its `CODEX_HOME`, `CLAUDE_CONFIG_DIR`, or `GROK_HOME` environment variable. Use absolute paths in the account's environment settings; relative environment paths depend on each project's working directory and cannot be reliably discovered -by Usage. Accounts sharing a history directory count once. +by Usage. Accounts sharing an identifiable history directory count once, while distinct directories +keep their own totals. If an older server cannot separate those totals, or a directory's filesystem +identity is unavailable, Usage warns that shared history may be counted more than once. Antigravity totals come from saved native conversations, including standalone CLI history when available. Models without known prices still contribute tokens; set a custom model price to include diff --git a/packages/contracts/src/usage.ts b/packages/contracts/src/usage.ts index 3506d733d6..33ff1a8bbe 100644 --- a/packages/contracts/src/usage.ts +++ b/packages/contracts/src/usage.ts @@ -21,7 +21,7 @@ import { NonNegativeInt, TrimmedNonEmptyString } from "./baseSchemas.ts"; * client renders partial coverage when an environment reports an older version * rather than failing the whole page. */ -export const USAGE_CONTRACT_VERSION = 6 as const; +export const USAGE_CONTRACT_VERSION = 7 as const; /** * Oldest {@link UsageSummary} version a current client will still merge. @@ -150,6 +150,12 @@ export const UsageSource = Schema.Struct({ * those overcounts; this is the figure clients should total. */ distinctSessions: NonNegativeInt, + /** + * Buckets attributed to this directory after the server's scan-wide record + * de-duplication. Added in v7; older peers only report provider-wide buckets. + * Raw transcript records never cross the wire. + */ + buckets: Schema.optional(Schema.Array(UsageBucket)), message: Schema.NullOr(TrimmedNonEmptyString), }); export type UsageSource = typeof UsageSource.Type; diff --git a/packages/shared/src/usageMerge.test.ts b/packages/shared/src/usageMerge.test.ts index 9c1346b3ce..60412b9f1d 100644 --- a/packages/shared/src/usageMerge.test.ts +++ b/packages/shared/src/usageMerge.test.ts @@ -41,6 +41,7 @@ function summary( homePath: string; volumeId?: string; distinctSessions?: number; + buckets?: readonly UsageBucket[]; }[], contractVersion: number = USAGE_CONTRACT_VERSION, ): UsageSummary { @@ -63,6 +64,7 @@ function summary( skippedFiles: 0, malformedRecords: 0, distinctSessions: source.distinctSessions ?? 1, + ...(source.buckets === undefined ? {} : { buckets: source.buckets }), message: null, })), pricing: { status: "fresh", source: "litellm", fetchedAt: null, knownModels: 10 }, @@ -75,6 +77,183 @@ function environment(id: string, usageSummary: UsageSummary): EnvironmentUsage { } describe("mergeUsage", () => { + it("keeps unique older homes while taking a newer shared-home scan", () => { + const shared = { provider: "claude" as const, hostId: "mac", homePath: "/shared" }; + const old = summary( + [bucket({ costUsd: 10 })], + [ + { ...shared, buckets: [bucket({ costUsd: 4 })] }, + { ...shared, homePath: "/unique-a", buckets: [bucket({ costUsd: 6 })] }, + ], + ); + const latest = summary( + [bucket({ costUsd: 16 })], + [ + { ...shared, buckets: [bucket({ costUsd: 5 })] }, + { ...shared, homePath: "/unique-b", buckets: [bucket({ costUsd: 11 })] }, + ], + ); + const merged = mergeUsage( + [ + environment("env-a", { ...old, readAt: "2026-08-07T00:00:00.000Z" }), + environment("env-b", { ...latest, readAt: "2026-08-08T00:00:00.000Z" }), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(22); + expect(merged.approximateEnvironments).toEqual([]); + }); + + it("counts overlapping Claude homes once while retaining both unique homes", () => { + const shared = { provider: "claude" as const, hostId: "mac", homePath: "/shared" }; + const uniqueA = { provider: "claude" as const, hostId: "mac", homePath: "/unique-a" }; + const uniqueB = { provider: "claude" as const, hostId: "mac", homePath: "/unique-b" }; + const four = bucket({ costUsd: 4, records: 1 }); + const six = bucket({ costUsd: 6, records: 1 }); + const eleven = bucket({ costUsd: 11, records: 1 }); + const merged = mergeUsage( + [ + environment( + "env-a", + summary( + [bucket({ costUsd: 10, records: 2 })], + [ + { ...shared, buckets: [four] }, + { ...uniqueA, buckets: [six] }, + ], + ), + ), + environment( + "env-b", + summary( + [bucket({ costUsd: 15, records: 2 })], + [ + { ...shared, buckets: [four] }, + { ...uniqueB, buckets: [eleven] }, + ], + ), + ), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(21); + expect(merged.records).toBe(3); + expect(merged.sessions).toBe(3); + expect(merged.duplicateSources).toHaveLength(1); + expect(merged.approximateEnvironments).toEqual([]); + }); + + it("does not collapse identical paths on different hosts or filesystems", () => { + const source = { provider: "claude" as const, hostId: "mac", homePath: "/same" }; + const merged = mergeUsage( + [ + environment( + "env-a", + summary([bucket({ costUsd: 4 })], [{ ...source, buckets: [bucket({ costUsd: 4 })] }]), + ), + environment( + "env-b", + summary( + [bucket({ costUsd: 6 })], + [{ ...source, hostId: "other", buckets: [bucket({ costUsd: 6 })] }], + ), + ), + environment( + "env-c", + summary( + [bucket({ costUsd: 11 })], + [{ ...source, volumeId: "moved-volume", buckets: [bucket({ costUsd: 11 })] }], + ), + ), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(21); + expect(merged.duplicateSources).toEqual([]); + }); + + it("keeps unknown filesystem identities separate and reports uncertain overlap", () => { + const source = { provider: "claude" as const, hostId: "mac", homePath: "/same" }; + const merged = mergeUsage( + [ + environment( + "env-a", + summary( + [bucket({ costUsd: 4 })], + [{ ...source, volumeId: "", buckets: [bucket({ costUsd: 4 })] }], + ), + ), + environment( + "env-b", + summary( + [bucket({ costUsd: 6 })], + [{ ...source, volumeId: "vol-mac", buckets: [bucket({ costUsd: 6 })] }], + ), + ), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(10); + expect(merged.duplicateSources).toEqual([]); + expect(merged.approximateEnvironments).toEqual(["env-a", "env-b"]); + }); + + it("marks legacy mixed-home totals approximate without dropping their unique usage", () => { + const shared = { provider: "claude" as const, hostId: "mac", homePath: "/shared" }; + const merged = mergeUsage( + [ + environment( + "env-a", + summary([bucket({ costUsd: 4 })], [{ ...shared, buckets: [bucket({ costUsd: 4 })] }]), + ), + environment( + "env-b", + summary( + [bucket({ costUsd: 15 })], + [shared, { ...shared, homePath: "/unique-b" }], + USAGE_CONTRACT_VERSION - 1, + ), + ), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(19); + expect(merged.approximateEnvironments).toEqual(["env-b"]); + expect(merged.staleEnvironments).toEqual([]); + }); + + it("falls back to flat provider totals when a source claims buckets for another provider", () => { + const shared = { provider: "claude" as const, hostId: "mac", homePath: "/shared" }; + const merged = mergeUsage( + [ + environment( + "env-a", + summary([bucket({ costUsd: 4 })], [{ ...shared, buckets: [bucket({ costUsd: 4 })] }]), + ), + environment( + "env-b", + summary( + [bucket({ costUsd: 15 })], + [ + { ...shared, buckets: [bucket({ provider: "codex", costUsd: 4 })] }, + { ...shared, homePath: "/unique-b", buckets: [bucket({ costUsd: 11 })] }, + ], + ), + ), + ], + USAGE_CONTRACT_VERSION, + ); + + expect(merged.costUsd).toBe(19); + expect(merged.providers.map((provider) => provider.provider)).toEqual(["claude"]); + expect(merged.approximateEnvironments).toEqual(["env-b"]); + }); + it("sums environments that read different transcript directories", () => { const merged = mergeUsage( [ diff --git a/packages/shared/src/usageMerge.ts b/packages/shared/src/usageMerge.ts index fee8e9c956..730b610a65 100644 --- a/packages/shared/src/usageMerge.ts +++ b/packages/shared/src/usageMerge.ts @@ -94,6 +94,8 @@ export interface MergedUsage { readonly duplicateSources: readonly string[]; readonly contributingEnvironments: readonly EnvironmentId[]; readonly staleEnvironments: readonly EnvironmentId[]; + /** Legacy mixed homes or unknown filesystem identities prevent exact attribution. */ + readonly approximateEnvironments: readonly EnvironmentId[]; } /** @@ -104,13 +106,16 @@ export interface MergedUsage { * home path, which is every Mac in a fleet, from collapsing into one source and * having one of them silently dropped. */ -function fingerprintKey(fingerprint: UsageSourceFingerprint): string { - return [ +function fingerprintKey(fingerprint: UsageSourceFingerprint, environmentId: EnvironmentId): string { + return JSON.stringify([ fingerprint.hostId, fingerprint.provider, fingerprint.resolvedHomePath, fingerprint.volumeId, - ].join(" "); + // An unreadable filesystem identity cannot prove two remote environments + // see the same directory, even if their hostnames and paths match. + fingerprint.volumeId === "" ? environmentId : "", + ]); } /** @@ -118,16 +123,17 @@ function fingerprintKey(fingerprint: UsageSourceFingerprint): string { * * Several environments on one machine (worktree servers, for instance) resolve * the same provider home and would otherwise double count every token. The - * most recently read summary claims a fingerprint; the rest have that provider's - * buckets dropped. Environment ids break ties so the winner is stable when - * summaries have the same read time. + * most recently read summary claims a fingerprint; environment ids break ties + * so ownership stays stable when two summaries have the same read time. */ function claimSources(environments: readonly EnvironmentUsage[]): { readonly ownerByFingerprint: ReadonlyMap; readonly duplicates: readonly string[]; + readonly uncertainEnvironments: readonly EnvironmentId[]; } { const ownerByFingerprint = new Map(); const duplicates: string[] = []; + const weakMatches = new Map(); const ordered = [...environments].sort( (a, b) => @@ -138,7 +144,18 @@ function claimSources(environments: readonly EnvironmentUsage[]): { for (const environment of ordered) { for (const source of environment.summary.sources) { if (source.status === "missing") continue; - const key = fingerprintKey(source.fingerprint); + const weakKey = JSON.stringify([ + source.fingerprint.hostId, + source.fingerprint.provider, + source.fingerprint.resolvedHomePath, + ]); + const matches = weakMatches.get(weakKey) ?? []; + matches.push({ + environmentId: environment.environmentId, + volumeId: source.fingerprint.volumeId, + }); + weakMatches.set(weakKey, matches); + const key = fingerprintKey(source.fingerprint, environment.environmentId); if (ownerByFingerprint.has(key)) { duplicates.push(`${environment.label}: ${source.fingerprint.resolvedHomePath}`); continue; @@ -147,7 +164,17 @@ function claimSources(environments: readonly EnvironmentUsage[]): { } } - return { ownerByFingerprint, duplicates }; + const uncertainEnvironments = new Set(); + for (const matches of weakMatches.values()) { + if ( + matches.some((match) => match.volumeId === "") && + new Set(matches.map((match) => match.environmentId)).size > 1 + ) { + for (const match of matches) uncertainEnvironments.add(match.environmentId); + } + } + + return { ownerByFingerprint, duplicates, uncertainEnvironments: [...uncertainEnvironments] }; } /** Sources this environment owns after fingerprint claims, plus their buckets. */ @@ -157,12 +184,16 @@ function ownedContribution( ): { readonly buckets: readonly UsageBucket[]; readonly sessionsByProvider: ReadonlyMap; + readonly approximate: boolean; } { const ownedProviders = new Set(); const sessionsByProvider = new Map(); + const sourcesByProvider = new Map(); for (const source of environment.summary.sources) { if (source.status === "missing") continue; - const key = fingerprintKey(source.fingerprint); + const providerSources = sourcesByProvider.get(source.fingerprint.provider) ?? []; + sourcesByProvider.set(source.fingerprint.provider, [...providerSources, source]); + const key = fingerprintKey(source.fingerprint, environment.environmentId); if (ownerByFingerprint.get(key) === environment.environmentId) { const provider = source.fingerprint.provider; ownedProviders.add(provider); @@ -174,9 +205,45 @@ function ownedContribution( ); } } + const buckets: UsageBucket[] = []; + let approximate = false; + for (const [provider, sources] of sourcesByProvider) { + if (!ownedProviders.has(provider)) continue; + // Mixed-version peers still have provider-wide buckets. Keep those totals + // visible, but explicitly report their attribution as approximate when a + // provider spans both owned and duplicated directories. + const granular = sources.every( + (source) => + source.buckets !== undefined && + source.buckets.every((bucket) => bucket.provider === source.fingerprint.provider), + ); + if (granular) { + for (const source of sources) { + if ( + ownerByFingerprint.get(fingerprintKey(source.fingerprint, environment.environmentId)) === + environment.environmentId + ) { + buckets.push(...(source.buckets ?? [])); + } + } + } else { + buckets.push(...environment.summary.buckets.filter((bucket) => bucket.provider === provider)); + if ( + sources.some( + (source) => + ownerByFingerprint.get( + fingerprintKey(source.fingerprint, environment.environmentId), + ) !== environment.environmentId, + ) + ) { + approximate = true; + } + } + } return { - buckets: environment.summary.buckets.filter((bucket) => ownedProviders.has(bucket.provider)), + buckets, sessionsByProvider, + approximate, }; } @@ -217,6 +284,7 @@ const EMPTY_MERGED: MergedUsage = { duplicateSources: [], contributingEnvironments: [], staleEnvironments: [], + approximateEnvironments: [], }; /** @@ -246,7 +314,7 @@ export function mergeUsage( } } - const { ownerByFingerprint, duplicates } = claimSources(current); + const { ownerByFingerprint, duplicates, uncertainEnvironments } = claimSources(current); let costUsd = 0; let uncachedInputTokens = 0; @@ -293,9 +361,14 @@ export function mergeUsage( } >(); const contributingEnvironments: EnvironmentId[] = []; + const approximateEnvironments = new Set(uncertainEnvironments); for (const environment of current) { - const { buckets, sessionsByProvider } = ownedContribution(environment, ownerByFingerprint); + const { buckets, sessionsByProvider, approximate } = ownedContribution( + environment, + ownerByFingerprint, + ); + if (approximate) approximateEnvironments.add(environment.environmentId); if (buckets.length > 0) contributingEnvironments.push(environment.environmentId); for (const [providerKind, providerSessions] of sessionsByProvider) { @@ -448,5 +521,6 @@ export function mergeUsage( duplicateSources: duplicates, contributingEnvironments, staleEnvironments, + approximateEnvironments: [...approximateEnvironments], }; } From 0c7c00fb3641c21c991b5ad4532b6ab46159c675 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:21:27 -0600 Subject: [PATCH 032/104] Start provider event worker before service exposure (#828) --- .../provider/Layers/ProviderService.test.ts | 29 +++++++++++++++++++ .../src/provider/Layers/ProviderService.ts | 4 ++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index 1e228af47d..a961d5ab50 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -57,6 +57,7 @@ import * as Option from "effect/Option"; import * as PubSub from "effect/PubSub"; import * as Ref from "effect/Ref"; import * as Result from "effect/Result"; +import * as Scheduler from "effect/Scheduler"; import * as Schema from "effect/Schema"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; @@ -6917,6 +6918,34 @@ class RuntimeReaper extends Context.Service()( describe("agent browser access", () => { const projectId = ProjectId.make("project-browser-access"); + it.effect("subscribes to adapter events before exposing the provider service", () => + Effect.gen(function* () { + const codex = makeFakeCodexAdapter(); + const events = yield* PubSub.unbounded(); + let subscribed = false; + const adapter: ProviderAdapterShape = { + ...codex.adapter, + streamEvents: Stream.unwrap( + Effect.map(PubSub.subscribe(events), (subscription) => { + // This runs only after PubSub has registered the subscription, not merely when + // the stream wrapper is constructed. + subscribed = true; + return Stream.fromSubscription(subscription); + }), + ), + }; + yield* Effect.gen(function* () { + yield* ProviderService.ProviderService; + assert.isTrue(subscribed); + }).pipe(Effect.provide(makeAgentBrowserProviderLayer(false, { ...codex, adapter }, {}))); + }).pipe( + // Keep this one fiber from yielding before the assertion. The old deferred fork then + // reproducibly leaves `subscribed` false; immediate startup passes without a timer. + Effect.provideService(Scheduler.PreventSchedulerYield, true), + Effect.provide(NodeServices.layer), + ), + ); + const makeBrowserAccessProjectionLayer = ( threadId: ThreadId, status: "found" | "missing" | "failed" = "found", diff --git a/apps/server/src/provider/Layers/ProviderService.ts b/apps/server/src/provider/Layers/ProviderService.ts index 2fc810c8a3..e999423390 100644 --- a/apps/server/src/provider/Layers/ProviderService.ts +++ b/apps/server/src/provider/Layers/ProviderService.ts @@ -1758,6 +1758,8 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( // Publish the new adapter set before any replacement stream can emit. yield* Ref.set(subscribedAdapters, next); for (const [id, adapter] of newSubscriptions) { + // A provider can emit a receipt synchronously during an API call. Subscribe before + // returning from reconciliation so that first receipt cannot outrun a deferred fork. yield* Stream.runForEach(adapter.streamEvents, (event) => processRuntimeEvent( { @@ -1767,7 +1769,7 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( }, event, ), - ).pipe(Effect.forkScoped); + ).pipe(Effect.forkScoped({ startImmediately: true })); } }); From e94af918305d7a47aa7feaad5570b56bf143fe88 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:21:52 -0600 Subject: [PATCH 033/104] fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures --- .../src/provider/Drivers/ClaudeDriver.ts | 35 +-- .../src/provider/Drivers/CodexDriver.test.ts | 147 +++++++---- .../src/provider/Drivers/CodexDriver.ts | 233 ++++++++++++------ .../src/provider/Layers/CodexProvider.test.ts | 35 ++- .../src/provider/Layers/CodexProvider.ts | 29 ++- .../provider/providerUsageRetention.test.ts | 167 +++++++++++-- .../src/provider/providerUsageRetention.ts | 85 ++++++- docs/user/usage.md | 4 +- 8 files changed, 558 insertions(+), 177 deletions(-) diff --git a/apps/server/src/provider/Drivers/ClaudeDriver.ts b/apps/server/src/provider/Drivers/ClaudeDriver.ts index 291589e252..a5461c1e18 100644 --- a/apps/server/src/provider/Drivers/ClaudeDriver.ts +++ b/apps/server/src/provider/Drivers/ClaudeDriver.ts @@ -12,11 +12,7 @@ * * @module provider/Drivers/ClaudeDriver */ -import { - ClaudeSettings, - ProviderDriverKind, - type ServerProviderUsageLimits, -} from "@t3tools/contracts"; +import { ClaudeSettings, ProviderDriverKind } from "@t3tools/contracts"; import * as Cache from "effect/Cache"; import * as Duration from "effect/Duration"; import * as Crypto from "effect/Crypto"; @@ -29,7 +25,12 @@ import * as Schema from "effect/Schema"; import { HttpClient } from "effect/unstable/http"; import { ChildProcessSpawner } from "effect/unstable/process"; -import { retainUsageLimits } from "../providerUsageRetention.ts"; +import { + authenticatedUsageIdentity, + matchingAccountUsage, + retainUsageLimitsForAccount, + type AccountUsageReading, +} from "../providerUsageRetention.ts"; import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts"; import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; import { ServerConfig } from "../../config.ts"; @@ -194,7 +195,7 @@ export const ClaudeDriver: ProviderDriver = { cwd, processEnv, ); - const lastKnownUsage = yield* Ref.make(undefined); + const lastKnownUsage = yield* Ref.make(undefined); const usageProbeCache = yield* Cache.makeWith( () => probeClaudeUsageLimits(effectiveConfig, processEnv, cwd).pipe( @@ -236,15 +237,17 @@ export const ClaudeDriver: ProviderDriver = { usageProbeCache, `${capabilitiesCacheKey}:${accountIdentity ?? "unknown"}`, ).pipe( - Effect.map((result) => { - if (!result) return undefined; - return result.accountIdentity && - accountIdentity && - result.accountIdentity !== accountIdentity - ? undefined - : result.usageLimits; - }), - Effect.flatMap((usageLimits) => retainUsageLimits(lastKnownUsage, usageLimits)), + Effect.map((result) => matchingAccountUsage(accountIdentity, result)), + Effect.flatMap((usageLimits) => + retainUsageLimitsForAccount( + lastKnownUsage, + authenticatedUsageIdentity({ + status: "authenticated", + email: accountIdentity, + }), + usageLimits, + ), + ), ), resolveClaudeModelCatalog(manifest), ), diff --git a/apps/server/src/provider/Drivers/CodexDriver.test.ts b/apps/server/src/provider/Drivers/CodexDriver.test.ts index 3020920d28..83bb4202e9 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.test.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.test.ts @@ -29,6 +29,7 @@ import { import { CodexDriver } from "./CodexDriver.ts"; import { writeFakeCli } from "../../testUtils/fakeCli.ts"; import { SHARED_USAGE_CACHE_DIR_ENV } from "../sharedUsageReadCache.ts"; +import { readSharedUsageEntry, sharedUsageReadKey } from "../sharedUsageReadCache.ts"; const encodeFixtureString = Schema.encodeEffect(Schema.fromJsonString(Schema.String)); @@ -61,32 +62,58 @@ const noSpawn = ChildProcessSpawner.make(() => ); it.layer(testLayer)("CodexDriver", (it) => { - for (const readFails of [false, true]) { - it.effect( - `redeems through the initialized CLI and ${readFails ? "preserves a confirmed result when refresh fails" : "reads fresh limits before returning"}`, - () => - Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const directory = yield* fs.makeTempDirectoryScoped({ prefix: "pylon-reset-credit-" }); - const logPath = NodePath.join(directory, "requests.jsonl"); - const priorCache = process.env[SHARED_USAGE_CACHE_DIR_ENV]; - yield* Effect.acquireRelease( + for (const scenario of [ + { + name: "reads fresh limits before returning", + readFails: false, + switchBefore: false, + switchOnConsume: false, + }, + { + name: "preserves a confirmed result when refresh fails", + readFails: true, + switchBefore: false, + switchOnConsume: false, + }, + { + name: "rejects a changed live account before consuming", + readFails: false, + switchBefore: true, + switchOnConsume: false, + }, + { + name: "does not cache a result after the account changes mid-reset", + readFails: false, + switchBefore: false, + switchOnConsume: true, + }, + ]) { + it.effect(`redeems through the initialized CLI and ${scenario.name}`, () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const directory = yield* fs.makeTempDirectoryScoped({ prefix: "pylon-reset-credit-" }); + const logPath = NodePath.join(directory, "requests.jsonl"); + const accountPath = NodePath.join(directory, "account.txt"); + yield* fs.writeFileString(accountPath, "owner@example.test"); + const priorCache = process.env[SHARED_USAGE_CACHE_DIR_ENV]; + yield* Effect.acquireRelease( + Effect.sync(() => { + process.env[SHARED_USAGE_CACHE_DIR_ENV] = NodePath.join(directory, "usage-cache"); + }), + () => Effect.sync(() => { - process.env[SHARED_USAGE_CACHE_DIR_ENV] = NodePath.join(directory, "usage-cache"); + if (priorCache === undefined) delete process.env[SHARED_USAGE_CACHE_DIR_ENV]; + else process.env[SHARED_USAGE_CACHE_DIR_ENV] = priorCache; }), - () => - Effect.sync(() => { - if (priorCache === undefined) delete process.env[SHARED_USAGE_CACHE_DIR_ENV]; - else process.env[SHARED_USAGE_CACHE_DIR_ENV] = priorCache; - }), - ); - const quotedLogPath = yield* encodeFixtureString(logPath); - const quotedDirectory = yield* encodeFixtureString(directory); - const binaryPath = writeFakeCli({ - directory, - name: "codex", - source: ` - import { appendFileSync } from "node:fs"; + ); + const quotedLogPath = yield* encodeFixtureString(logPath); + const quotedDirectory = yield* encodeFixtureString(directory); + const quotedAccountPath = yield* encodeFixtureString(accountPath); + const binaryPath = writeFakeCli({ + directory, + name: "codex", + source: ` + import { appendFileSync, readFileSync, writeFileSync } from "node:fs"; import { createInterface } from "node:readline"; const lines = createInterface({ input: process.stdin }); lines.on("line", line => { @@ -95,9 +122,14 @@ it.layer(testLayer)("CodexDriver", (it) => { if (request.id === undefined) return; let result; if (request.method === "initialize") result = { userAgent: "codex/1.0", codexHome: ${quotedDirectory}, platformFamily: "unix", platformOs: "macos" }; - else if (request.method === "account/rateLimitResetCredit/consume") result = { outcome: "reset" }; + else if (request.method === "account/read") result = { account: { type: "chatgpt", email: readFileSync(${quotedAccountPath}, "utf8"), planType: "plus" }, requiresOpenaiAuth: false }; + else if (request.method === "skills/list" || request.method === "model/list") result = { data: [] }; + else if (request.method === "account/rateLimitResetCredit/consume") { + if (${scenario.switchOnConsume}) writeFileSync(${quotedAccountPath}, "other@example.test"); + result = { outcome: "reset" }; + } else if (request.method === "account/rateLimits/read") { - if (${readFails}) { + if (${scenario.readFails}) { process.stdout.write(JSON.stringify({ id: request.id, error: { code: -32000, message: "read failed" } }) + "\\n"); return; } @@ -106,28 +138,59 @@ it.layer(testLayer)("CodexDriver", (it) => { process.stdout.write(JSON.stringify({ id: request.id, result }) + "\\n"); }); `, - }); - const instance = yield* CodexDriver.create({ - instanceId: ProviderInstanceId.make("codex-reset-test"), - displayName: "Reset test", - enabled: false, - environment: [], - config: { ...CodexDriver.defaultConfig(), binaryPath, homePath: directory }, - }); - const result = yield* instance.consumeResetCredit!({ requestId: "stable-reset-attempt" }); - expect(result.outcome).toBe("reset"); - if (readFails) expect(result.warning).toContain("updated limits could not be read"); - else expect(result.warning).toBeUndefined(); - const requests = yield* fs.readFileString(logPath); - expect(requests).toContain('"method":"initialized"'); + }); + const instance = yield* CodexDriver.create({ + instanceId: ProviderInstanceId.make("codex-reset-test"), + displayName: "Reset test", + enabled: true, + environment: [], + config: { ...CodexDriver.defaultConfig(), binaryPath, homePath: directory }, + }); + yield* instance.snapshot.refresh; + const accountCacheKey = sharedUsageReadKey([ + "codex", + directory, + "email:owner@example.test", + ]); + const cacheBefore = yield* readSharedUsageEntry( + NodePath.join(directory, "usage-cache"), + accountCacheKey, + ); + if (scenario.switchBefore) yield* fs.writeFileString(accountPath, "other@example.test"); + const result = yield* instance.consumeResetCredit!({ + requestId: "stable-reset-attempt", + }).pipe(Effect.exit); + if (scenario.switchBefore) expect(result._tag).toBe("Failure"); + else { + expect(result._tag).toBe("Success"); + if (result._tag === "Success") { + expect(result.value.outcome).toBe("reset"); + if (scenario.readFails) + expect(result.value.warning).toContain("updated limits could not be read"); + else if (scenario.switchOnConsume) + expect(result.value.warning).toContain("account changed"); + else expect(result.value.warning).toBeUndefined(); + } + } + const requests = yield* fs.readFileString(logPath); + expect(requests).toContain('"method":"initialized"'); + if (scenario.switchBefore) { + expect(requests).not.toContain('"method":"account/rateLimitResetCredit/consume"'); + } else { expect(requests).toContain('"idempotencyKey":"stable-reset-attempt"'); expect(requests.indexOf('"method":"initialize"')).toBeLessThan( requests.indexOf('"method":"account/rateLimitResetCredit/consume"'), ); expect(requests.indexOf('"method":"account/rateLimitResetCredit/consume"')).toBeLessThan( - requests.indexOf('"method":"account/rateLimits/read"'), + requests.lastIndexOf('"method":"account/rateLimits/read"'), ); - }).pipe(Effect.scoped), + } + if (scenario.switchBefore || scenario.switchOnConsume) { + expect( + yield* readSharedUsageEntry(NodePath.join(directory, "usage-cache"), accountCacheKey), + ).toEqual(cacheBefore); + } + }).pipe(Effect.scoped), ); } diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index a258ac4c06..873f2d9047 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -21,11 +21,7 @@ * * @module provider/Drivers/CodexDriver */ -import { - CodexSettings, - ProviderDriverKind, - type ServerProviderUsageLimits, -} from "@t3tools/contracts"; +import { CodexSettings, ProviderDriverKind } from "@t3tools/contracts"; import * as DateTime from "effect/DateTime"; import * as Crypto from "effect/Crypto"; import * as Effect from "effect/Effect"; @@ -44,11 +40,13 @@ import { ServerSettingsService } from "../../serverSettings.ts"; import { ProviderDriverError } from "../Errors.ts"; import { makeCodexAdapter } from "../Layers/CodexAdapter.ts"; import { + accountProbeStatus, checkCodexProviderStatus, makePendingCodexProvider, probeCodexSkillsForCwd, withCodexAppServerClient, } from "../Layers/CodexProvider.ts"; +import { readCodexAccountId } from "../codexAccountIdentity.ts"; import { CodexResetCreditCoordinator, CODEX_RESET_CREDIT_TIMEOUT, @@ -78,7 +76,13 @@ import { makeProviderSnapshotSettingsSource, type ProviderSnapshotSettings, } from "../providerUpdateSettings.ts"; -import { retainSnapshotUsageLimits, preferFreshUsageReading } from "../providerUsageRetention.ts"; +import { + authenticatedUsageIdentity, + retainSnapshotUsageLimits, + preferFreshUsageReading, + usageReadingForAuth, + type AccountUsageReading, +} from "../providerUsageRetention.ts"; import { codexContinuationIdentity, materializeCodexShadowHome, @@ -211,8 +215,8 @@ export const CodexDriver: ProviderDriver = { // Subscription capacity rides the same probe as install/auth status but // is read over the network, so it fails independently. Keep the last // good reading through those failures rather than blanking the gauge. - const lastKnownUsage = yield* Ref.make(undefined); - const redeemedUsage = yield* Ref.make(undefined); + const lastKnownUsage = yield* Ref.make(undefined); + const redeemedUsage = yield* Ref.make(undefined); const checkProvider = modelManifest.refreshInBackground.pipe( Effect.andThen( Effect.zipWith( @@ -221,7 +225,11 @@ export const CodexDriver: ProviderDriver = { Effect.gen(function* () { const resetReading = yield* Ref.get(redeemedUsage); const nowMs = yield* Effect.clockWith((clock) => clock.currentTimeMillis); - const published = preferFreshUsageReading(snapshot, resetReading, nowMs); + const published = preferFreshUsageReading( + snapshot, + usageReadingForAuth(resetReading, snapshot.auth), + nowMs, + ); return yield* retainSnapshotUsageLimits(lastKnownUsage, published); }), ), @@ -307,77 +315,148 @@ export const CodexDriver: ProviderDriver = { const accountKey = homeLayout.effectiveHomePath ?? homeLayout.sharedHomePath; const consumeResetCredit: NonNullable = (input) => - resetCreditCoordinator - .redeem(accountKey, input.requestId, (idempotencyKey) => - Effect.gen(function* () { - const result = yield* Effect.gen(function* () { - const { client, outcome } = yield* Effect.gen(function* () { - const { client } = yield* withCodexAppServerClient({ - binaryPath: effectiveConfig.binaryPath, - homePath: effectiveConfig.homePath, - launchArgs: resolveCodexLaunchArgs(effectiveConfig.launchArgs, processEnv), - cwd: process.cwd(), - environment: processEnv, - }); - const { outcome } = yield* client.request( - "account/rateLimitResetCredit/consume", - { idempotencyKey }, - ); - return { client, outcome }; - }).pipe(Effect.timeout(CODEX_RESET_CREDIT_TIMEOUT)); - const reading = yield* client - .request("account/rateLimits/read", undefined) - .pipe(Effect.timeout("5 seconds"), Effect.result); - if (reading._tag === "Failure") - return { - outcome, - warning: - "Codex answered the reset request, but its updated limits could not be read. Refresh to check.", - }; - const checkedAt = DateTime.formatIso(yield* DateTime.now); - const limits = usageLimitsFromCodexRateLimits(reading.success, checkedAt); - if (!limits) - return { - outcome, - warning: - "Codex answered the reset request without reporting updated limits. Refresh to check.", - }; - yield* Ref.set(redeemedUsage, limits); - yield* writeSharedUsageEntry( - yield* resolveSharedUsageCacheDir, - sharedUsageReadKey(["codex", accountKey]), - { version: 1, readAt: checkedAt, usageLimits: limits }, + Effect.gen(function* () { + const selectedIdentity = authenticatedUsageIdentity((yield* snapshot.getSnapshot).auth); + if (!selectedIdentity) { + return yield* Effect.fail( + new ProviderDriverError({ + driver: DRIVER_KIND, + instanceId, + detail: "Codex account identity is not verified.", + }), + ); + } + // A pending or completed attempt belongs to one account, even if the + // same CLI home signs in as another account before a retry. + const attemptKey = sharedUsageReadKey(["codex-reset", accountKey, selectedIdentity]); + return yield* resetCreditCoordinator.redeem( + attemptKey, + input.requestId, + (idempotencyKey) => + Effect.gen(function* () { + const identityBefore = authenticatedUsageIdentity( + (yield* snapshot.getSnapshot).auth, ); - return { outcome }; - }).pipe(Effect.scoped); - // The direct account read bypasses the shared cache. Even if an older - // status probe finishes now, checkProvider overlays this fresh reading. - const refreshed = yield* snapshot.refresh.pipe(Effect.exit); - return refreshed._tag === "Failure" && !result.warning - ? { - ...result, - warning: - "Codex answered the reset request, but the provider status could not refresh. Refresh to check.", + if (identityBefore !== selectedIdentity) { + return yield* Effect.fail( + new ProviderDriverError({ + driver: DRIVER_KIND, + instanceId, + detail: "Codex account changed before the reset credit could be used.", + }), + ); + } + const result = yield* Effect.gen(function* () { + const { client, sharedHomePath, outcome } = yield* Effect.gen(function* () { + const { client, sharedHomePath } = yield* withCodexAppServerClient({ + binaryPath: effectiveConfig.binaryPath, + homePath: effectiveConfig.homePath, + launchArgs: resolveCodexLaunchArgs(effectiveConfig.launchArgs, processEnv), + cwd: process.cwd(), + environment: processEnv, + }); + const liveAccount = yield* client.request("account/read", {}); + const liveIdentity = authenticatedUsageIdentity( + accountProbeStatus(liveAccount, yield* readCodexAccountId(sharedHomePath)) + .auth, + ); + if (!identityBefore || liveIdentity !== identityBefore) { + return yield* Effect.fail( + new ProviderDriverError({ + driver: DRIVER_KIND, + instanceId, + detail: "Codex account changed before the reset credit could be used.", + }), + ); + } + const { outcome } = yield* client.request( + "account/rateLimitResetCredit/consume", + { idempotencyKey }, + ); + return { client, sharedHomePath, outcome }; + }).pipe(Effect.timeout(CODEX_RESET_CREDIT_TIMEOUT)); + const reading = yield* client + .request("account/rateLimits/read", undefined) + .pipe(Effect.timeout("5 seconds"), Effect.result); + if (reading._tag === "Failure") + return { + outcome, + warning: + "Codex answered the reset request, but its updated limits could not be read. Refresh to check.", + }; + const checkedAt = DateTime.formatIso(yield* DateTime.now); + const limits = usageLimitsFromCodexRateLimits(reading.success, checkedAt); + if (!limits) + return { + outcome, + warning: + "Codex answered the reset request without reporting updated limits. Refresh to check.", + }; + const accountAfter = yield* client + .request("account/read", {}) + .pipe(Effect.option); + const liveIdentityAfter = + accountAfter._tag === "Some" + ? authenticatedUsageIdentity( + accountProbeStatus( + accountAfter.value, + yield* readCodexAccountId(sharedHomePath), + ).auth, + ) + : undefined; + const snapshotIdentityAfter = authenticatedUsageIdentity( + (yield* snapshot.getSnapshot).auth, + ); + if ( + identityBefore && + liveIdentityAfter === identityBefore && + snapshotIdentityAfter === identityBefore + ) { + yield* Ref.set(redeemedUsage, { + identity: identityBefore, + usageLimits: limits, + }); + yield* writeSharedUsageEntry( + yield* resolveSharedUsageCacheDir, + sharedUsageReadKey(["codex", accountKey, identityBefore]), + { version: 1, readAt: checkedAt, usageLimits: limits }, + ); + } else { + return { + outcome, + warning: + "Codex answered the reset request, but the account changed before its limits could be verified. Refresh to check.", + }; } - : result; - }).pipe( - Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), - Effect.provideService(FileSystem.FileSystem, fileSystem), - Effect.provideService(Path.Path, path), - ), - ) - .pipe( - Effect.mapError( - (cause) => - new ProviderDriverError({ - driver: DRIVER_KIND, - instanceId, - detail: - "Codex could not redeem the reset credit. Retry to check the same attempt.", - cause, - }), - ), + return { outcome }; + }).pipe(Effect.scoped); + // The direct account read bypasses the shared cache. Even if an older + // status probe finishes now, checkProvider overlays this fresh reading. + const refreshed = yield* snapshot.refresh.pipe(Effect.exit); + return refreshed._tag === "Failure" && !result.warning + ? { + ...result, + warning: + "Codex answered the reset request, but the provider status could not refresh. Refresh to check.", + } + : result; + }).pipe( + Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), + Effect.provideService(FileSystem.FileSystem, fileSystem), + Effect.provideService(Path.Path, path), + ), ); + }).pipe( + Effect.mapError( + (cause) => + new ProviderDriverError({ + driver: DRIVER_KIND, + instanceId, + detail: "Codex could not redeem the reset credit. Retry to check the same attempt.", + cause, + }), + ), + ); return { instanceId, diff --git a/apps/server/src/provider/Layers/CodexProvider.test.ts b/apps/server/src/provider/Layers/CodexProvider.test.ts index 94cd8ba582..fe319f45a9 100644 --- a/apps/server/src/provider/Layers/CodexProvider.test.ts +++ b/apps/server/src/provider/Layers/CodexProvider.test.ts @@ -240,6 +240,8 @@ it("labels non-ChatGPT account types", () => { }); const CODEX_HOME = "/home/someone/.codex"; +const ACCOUNT_A = "email:a@example.com"; +const ACCOUNT_B = "email:b@example.com"; const usageAt = (checkedAt: string): ServerProviderUsageLimits => ({ source: "codexAppServer", @@ -272,7 +274,7 @@ const readAfterMissedRead = (input: { readonly age: Duration.Duration }) => else process.env[SHARED_USAGE_CACHE_DIR_ENV] = priorCache; }), ); - const cacheKey = sharedUsageReadKey(["codex", CODEX_HOME]); + const cacheKey = sharedUsageReadKey(["codex", CODEX_HOME, ACCOUNT_A]); const readAt = DateTime.formatIso(DateTime.makeUnsafe(0)); yield* writeSharedUsageEntry(cacheDir, cacheKey, { version: 1, @@ -283,6 +285,7 @@ const readAfterMissedRead = (input: { readonly age: Duration.Duration }) => yield* TestClock.adjust(input.age); const result = yield* readCodexRateLimitsShared({ sharedHomePath: CODEX_HOME, + accountIdentity: ACCOUNT_A, read: MISSED_READ, }); return { result, entry: yield* readSharedUsageEntry(cacheDir, cacheKey), readAt }; @@ -311,4 +314,34 @@ it.layer(NodeServices.layer)("codex capacity across a missed read", (it) => { assert.deepStrictEqual(result, {}); }), ); + + it.effect("does not read account A's shared quota after the same home signs into B", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const cacheDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "pylon-codex-usage-" }); + const priorCache = process.env[SHARED_USAGE_CACHE_DIR_ENV]; + yield* Effect.acquireRelease( + Effect.sync(() => { + process.env[SHARED_USAGE_CACHE_DIR_ENV] = cacheDir; + }), + () => + Effect.sync(() => { + if (priorCache === undefined) delete process.env[SHARED_USAGE_CACHE_DIR_ENV]; + else process.env[SHARED_USAGE_CACHE_DIR_ENV] = priorCache; + }), + ); + const readAt = DateTime.formatIso(yield* DateTime.now); + yield* writeSharedUsageEntry(cacheDir, sharedUsageReadKey(["codex", CODEX_HOME, ACCOUNT_A]), { + version: 1, + readAt, + usageLimits: usageAt(readAt), + }); + const result = yield* readCodexRateLimitsShared({ + sharedHomePath: CODEX_HOME, + accountIdentity: ACCOUNT_B, + read: MISSED_READ, + }); + assert.deepStrictEqual(result, {}); + }).pipe(Effect.scoped), + ); }); diff --git a/apps/server/src/provider/Layers/CodexProvider.ts b/apps/server/src/provider/Layers/CodexProvider.ts index 871578cd02..b52b6fe3b2 100644 --- a/apps/server/src/provider/Layers/CodexProvider.ts +++ b/apps/server/src/provider/Layers/CodexProvider.ts @@ -55,7 +55,7 @@ import { sharedUsageReadKey, writeSharedUsageEntry, } from "../sharedUsageReadCache.ts"; -import { isRetainedUsageFresh } from "../providerUsageRetention.ts"; +import { authenticatedUsageIdentity, isRetainedUsageFresh } from "../providerUsageRetention.ts"; import packageJson from "../../../package.json" with { type: "json" }; const isCodexAppServerSpawnError = Schema.is(CodexErrors.CodexAppServerSpawnError); @@ -402,14 +402,22 @@ const failedRead = Effect.fn("readCodexRateLimitsShared.failed")(function* ( */ export const readCodexRateLimitsShared = Effect.fn("readCodexRateLimitsShared")(function* (input: { readonly sharedHomePath: string; + readonly accountIdentity: string | undefined; readonly read: Effect.Effect; }): Effect.fn.Return< Pick, never, FileSystem.FileSystem | Path.Path > { + // A home can sign out and into another account without changing path. An + // unidentified account may make a live read, but must never reuse the old + // home's shared quota file. + if (!input.accountIdentity) { + const rateLimits = yield* input.read; + return rateLimits ? { rateLimits } : {}; + } const cacheDir = yield* resolveSharedUsageCacheDir; - const cacheKey = sharedUsageReadKey(["codex", input.sharedHomePath]); + const cacheKey = sharedUsageReadKey(["codex", input.sharedHomePath, input.accountIdentity]); const nowMs = yield* Effect.clockWith((clock) => clock.currentTimeMillis); const shared = decideSharedUsageRead(yield* readSharedUsageEntry(cacheDir, cacheKey), nowMs); if (shared.kind === "fresh") return { sharedUsageLimits: shared.usageLimits }; @@ -526,11 +534,14 @@ const probeCodexAppServerProvider = Effect.fn("probeCodexAppServerProvider")(fun } satisfies CodexAppServerProviderSnapshot; } - // The account id is read alongside the app-server requests rather than - // before the spawn: the probe's timeout is measured from its start, and a - // filesystem read ahead of the spawn would let it expire with nothing to - // close. - const [skillsResponse, models, rateLimitsRead, accountId] = yield* Effect.all( + // Read the account id after the app-server starts, then bind the shared + // quota file to this account before looking it up. A home path alone is + // reused after sign-out and cannot identify a subscription. + const accountId = yield* readCodexAccountId(sharedHomePath); + const accountIdentity = authenticatedUsageIdentity( + accountProbeStatus(accountResponse, accountId).auth, + ); + const [skillsResponse, models, rateLimitsRead] = yield* Effect.all( [ client.request("skills/list", { cwds: [input.cwd], @@ -539,6 +550,7 @@ const probeCodexAppServerProvider = Effect.fn("probeCodexAppServerProvider")(fun accountResponse.account?.type === "chatgpt" ? readCodexRateLimitsShared({ sharedHomePath, + accountIdentity, read: client .request("account/rateLimits/read", undefined) .pipe( @@ -551,7 +563,6 @@ const probeCodexAppServerProvider = Effect.fn("probeCodexAppServerProvider")(fun : Effect.succeed>( {}, ), - readCodexAccountId(sharedHomePath), ], { concurrency: "unbounded" }, ); @@ -659,7 +670,7 @@ const makePendingCodexProvider = ( }); }); -function accountProbeStatus( +export function accountProbeStatus( account: CodexAppServerProviderSnapshot["account"], accountId: string | undefined, ): { diff --git a/apps/server/src/provider/providerUsageRetention.test.ts b/apps/server/src/provider/providerUsageRetention.test.ts index 1a43b35335..e82dfbdd7e 100644 --- a/apps/server/src/provider/providerUsageRetention.test.ts +++ b/apps/server/src/provider/providerUsageRetention.test.ts @@ -5,10 +5,14 @@ import * as Ref from "effect/Ref"; import * as TestClock from "effect/testing/TestClock"; import { + authenticatedUsageIdentity, isRetainedUsageFresh, + matchingAccountUsage, retainSnapshotUsageLimits, - retainUsageLimits, + retainUsageLimitsForAccount, preferFreshUsageReading, + usageReadingForAuth, + type AccountUsageReading, } from "./providerUsageRetention.ts"; const NOW = Date.parse("2026-08-06T12:00:00.000Z"); @@ -71,17 +75,67 @@ describe("isRetainedUsageFresh", () => { }); }); -describe("retainUsageLimits", () => { +const ACCOUNT_A = "email:a@example.com"; +const ACCOUNT_B = "email:b@example.com"; + +it("binds an authenticated Codex reading to both account id and live email", () => { + assert.strictEqual( + authenticatedUsageIdentity({ + status: "authenticated", + accountId: "account-1", + email: "A@Example.com", + }), + "account:account-1|email:a@example.com", + ); + assert.isUndefined(authenticatedUsageIdentity({ status: "unknown", email: "a@example.com" })); +}); + +it("does not overlay a direct reset reading onto another or unverified account", () => { + const direct = { identity: ACCOUNT_A, usageLimits: usage("2026-08-06T11:59:00.000Z") }; + assert.strictEqual( + usageReadingForAuth(direct, { status: "authenticated", email: "a@example.com" }), + direct.usageLimits, + ); + assert.isUndefined( + usageReadingForAuth(direct, { status: "authenticated", email: "b@example.com" }), + ); + assert.isUndefined(usageReadingForAuth(direct, { status: "unknown" })); +}); + +it("accepts a Claude OAuth read only when its account probe agrees with the current account", () => { + const limits = usage("2026-08-06T11:59:00.000Z"); + assert.strictEqual( + matchingAccountUsage("B@Example.com", { + accountIdentity: "b@example.com", + usageLimits: limits, + }), + limits, + ); + assert.isUndefined( + matchingAccountUsage("b@example.com", { + accountIdentity: "a@example.com", + usageLimits: limits, + }), + ); + assert.isUndefined( + matchingAccountUsage("b@example.com", { accountIdentity: undefined, usageLimits: limits }), + ); +}); + +describe("retainUsageLimitsForAccount", () => { it.effect("remembers a successful reading", () => Effect.gen(function* () { yield* TestClock.setTime(NOW); - const lastKnown = yield* Ref.make(undefined); + const lastKnown = yield* Ref.make(undefined); const fresh = usage("2026-08-06T11:59:00.000Z"); - const result = yield* retainUsageLimits(lastKnown, fresh); + const result = yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, fresh); assert.strictEqual(result, fresh); - assert.strictEqual(yield* Ref.get(lastKnown), fresh); + assert.deepStrictEqual(yield* Ref.get(lastKnown), { + identity: ACCOUNT_A, + usageLimits: fresh, + }); }), ); @@ -90,9 +144,12 @@ describe("retainUsageLimits", () => { Effect.gen(function* () { yield* TestClock.setTime(NOW); const previous = usage("2026-08-06T11:59:00.000Z"); - const lastKnown = yield* Ref.make(previous); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: previous, + }); - const result = yield* retainUsageLimits(lastKnown, undefined); + const result = yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, undefined); assert.strictEqual(result, previous); }), @@ -102,9 +159,12 @@ describe("retainUsageLimits", () => { Effect.gen(function* () { yield* TestClock.setTime(NOW); const ancient = usage("2020-01-01T00:00:00.000Z"); - const lastKnown = yield* Ref.make(ancient); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: ancient, + }); - const result = yield* retainUsageLimits(lastKnown, undefined); + const result = yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, undefined); assert.isUndefined(result); // Cleared as well, so it cannot resurface later. @@ -114,9 +174,53 @@ describe("retainUsageLimits", () => { it.effect("has nothing to show before any reading succeeds", () => Effect.gen(function* () { - const lastKnown = yield* Ref.make(undefined); + const lastKnown = yield* Ref.make(undefined); + + assert.isUndefined(yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, undefined)); + }), + ); + + it.effect("clears account A after an authenticated switch to B with a failed read", () => + Effect.gen(function* () { + yield* TestClock.setTime(NOW); + const previous = usage("2026-08-06T11:59:00.000Z"); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: previous, + }); + assert.isUndefined(yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_B, undefined)); + assert.isUndefined(yield* Ref.get(lastKnown)); + assert.isUndefined(yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, undefined)); + }), + ); + + it.effect("does not retain an unidentified account's reading", () => + Effect.gen(function* () { + const lastKnown = yield* Ref.make(undefined); + const fresh = usage("2026-08-06T11:59:00.000Z"); + assert.strictEqual(yield* retainUsageLimitsForAccount(lastKnown, undefined, fresh), fresh); + assert.isUndefined(yield* Ref.get(lastKnown)); + }), + ); - assert.isUndefined(yield* retainUsageLimits(lastKnown, undefined)); + it.effect("hides an A reading during unknown auth, then reuses it only for verified A", () => + Effect.gen(function* () { + yield* TestClock.setTime(NOW); + const previous = usage("2026-08-06T11:59:00.000Z"); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: previous, + }); + assert.isUndefined(yield* retainUsageLimitsForAccount(lastKnown, undefined, undefined)); + assert.deepStrictEqual(yield* Ref.get(lastKnown), { + identity: ACCOUNT_A, + usageLimits: previous, + }); + assert.strictEqual( + yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_A, undefined), + previous, + ); + assert.isUndefined(yield* retainUsageLimitsForAccount(lastKnown, ACCOUNT_B, undefined)); }), ); }); @@ -124,9 +228,10 @@ describe("retainUsageLimits", () => { describe("retainSnapshotUsageLimits", () => { const snapshot = (input: { readonly auth: "authenticated" | "unauthenticated" | "unknown"; + readonly email?: string; readonly usageLimits?: ServerProviderUsageLimits; }) => ({ - auth: { status: input.auth }, + auth: { status: input.auth, ...(input.email ? { email: input.email } : {}) }, ...(input.usageLimits ? { usageLimits: input.usageLimits } : {}), }); @@ -136,11 +241,14 @@ describe("retainSnapshotUsageLimits", () => { Effect.gen(function* () { yield* TestClock.setTime(NOW); const previous = usage("2026-08-06T11:59:00.000Z"); - const lastKnown = yield* Ref.make(previous); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: previous, + }); const result = yield* retainSnapshotUsageLimits( lastKnown, - snapshot({ auth: "authenticated" }), + snapshot({ auth: "authenticated", email: "a@example.com" }), ); assert.strictEqual(result.usageLimits, previous); @@ -150,16 +258,20 @@ describe("retainSnapshotUsageLimits", () => { it.effect("returns the probe untouched when it carries its own reading", () => Effect.gen(function* () { yield* TestClock.setTime(NOW); - const lastKnown = yield* Ref.make(undefined); + const lastKnown = yield* Ref.make(undefined); const probed = snapshot({ auth: "authenticated", + email: "a@example.com", usageLimits: usage("2026-08-06T11:59:00.000Z"), }); const result = yield* retainSnapshotUsageLimits(lastKnown, probed); assert.strictEqual(result, probed); - assert.strictEqual(yield* Ref.get(lastKnown), probed.usageLimits); + assert.deepStrictEqual(yield* Ref.get(lastKnown), { + identity: ACCOUNT_A, + usageLimits: probed.usageLimits, + }); }), ); @@ -168,9 +280,10 @@ describe("retainSnapshotUsageLimits", () => { it.effect("clears the reading when the account signs out", () => Effect.gen(function* () { yield* TestClock.setTime(NOW); - const lastKnown = yield* Ref.make( - usage("2026-08-06T11:59:00.000Z"), - ); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: usage("2026-08-06T11:59:00.000Z"), + }); const result = yield* retainSnapshotUsageLimits( lastKnown, @@ -181,4 +294,20 @@ describe("retainSnapshotUsageLimits", () => { assert.strictEqual(yield* Ref.get(lastKnown), undefined); }), ); + + it.effect("does not attach account A's reading to an authenticated B snapshot", () => + Effect.gen(function* () { + yield* TestClock.setTime(NOW); + const lastKnown = yield* Ref.make({ + identity: ACCOUNT_A, + usageLimits: usage("2026-08-06T11:59:00.000Z"), + }); + const result = yield* retainSnapshotUsageLimits( + lastKnown, + snapshot({ auth: "authenticated", email: "b@example.com" }), + ); + assert.isUndefined(result.usageLimits); + assert.isUndefined(yield* Ref.get(lastKnown)); + }), + ); }); diff --git a/apps/server/src/provider/providerUsageRetention.ts b/apps/server/src/provider/providerUsageRetention.ts index b9e1b5a3c3..5fb3384d2e 100644 --- a/apps/server/src/provider/providerUsageRetention.ts +++ b/apps/server/src/provider/providerUsageRetention.ts @@ -58,32 +58,89 @@ export function isRetainedUsageFresh(input: { * number beats no number when deciding where to send work, and `checkedAt` * travels with it so the client can say how old it is. */ -export function retainUsageLimits( - lastKnown: Ref.Ref, +export interface AccountUsageReading { + readonly identity: string; + readonly usageLimits: ServerProviderUsageLimits; +} + +/** Only stable, affirmative account identity can authorize a retained reading. */ +export function authenticatedUsageIdentity(auth: ServerProviderAuth): string | undefined { + if (auth.status !== "authenticated") return undefined; + // Account id and live CLI email are independent signals. Keeping both + // prevents a changing auth.json from assigning B's CLI read to A's id. + if (auth.accountId && auth.email) { + return `account:${auth.accountId}|email:${auth.email.trim().toLowerCase()}`; + } + if (auth.accountId) return `account:${auth.accountId}`; + if (auth.email) return `email:${auth.email.trim().toLowerCase()}`; + return undefined; +} + +/** A direct post-reset read may overlay only the same authenticated account. */ +export function usageReadingForAuth( + reading: AccountUsageReading | undefined, + auth: ServerProviderAuth, +): ServerProviderUsageLimits | undefined { + const identity = authenticatedUsageIdentity(auth); + return identity && reading?.identity === identity ? reading.usageLimits : undefined; +} + +/** A separate OAuth usage read must agree with the authenticated account. */ +export function matchingAccountUsage( + expectedEmail: string | undefined, + result: + | { + readonly accountIdentity: string | undefined; + readonly usageLimits: ServerProviderUsageLimits | undefined; + } + | undefined, +): ServerProviderUsageLimits | undefined { + if (!result) return undefined; + if ( + expectedEmail && + result.accountIdentity?.trim().toLowerCase() !== expectedEmail.trim().toLowerCase() + ) { + return undefined; + } + return result.usageLimits; +} + +export function retainUsageLimitsForAccount( + lastKnown: Ref.Ref, + identity: string | undefined, usageLimits: ServerProviderUsageLimits | undefined, ): Effect.Effect { return Effect.gen(function* () { if (usageLimits) { - yield* RefModule.set(lastKnown, usageLimits); + if (identity) yield* RefModule.set(lastKnown, { identity, usageLimits }); return usageLimits; } const retained = yield* RefModule.get(lastKnown); if (!retained) return undefined; + // A temporarily unknown auth result cannot authorize publication, but it + // also cannot prove the signed-in account changed. Hold the reading for a + // later matching identity without showing it during the unknown interval. + if (identity === undefined) return undefined; + if (retained.identity !== identity) { + yield* RefModule.set(lastKnown, undefined); + return undefined; + } const nowMs = yield* Effect.clockWith((clock) => clock.currentTimeMillis); - if (isRetainedUsageFresh({ checkedAt: retained.checkedAt, nowMs })) return retained; + if (isRetainedUsageFresh({ checkedAt: retained.usageLimits.checkedAt, nowMs })) { + return retained.usageLimits; + } yield* RefModule.set(lastKnown, undefined); return undefined; }); } /** - * Apply {@link retainUsageLimits} to a whole probed snapshot. + * Apply account-scoped retention to a whole probed snapshot. * - * A signed-out account has no capacity to retain, so its reading is cleared - * rather than carried; anything else keeps the last good reading through a - * failed or timed-out read. Codex reads its windows over the network inside - * the status probe, where one slow answer would otherwise blank the gauge - * until the next poll. + * A signed-out account has no capacity to retain. A failed or timed-out read + * may reuse the last reading only while the authenticated account identity + * remains the same. Codex reads its windows over the network inside the + * status probe, where one slow answer would otherwise blank the gauge. */ export function retainSnapshotUsageLimits< Snapshot extends { @@ -91,7 +148,7 @@ export function retainSnapshotUsageLimits< readonly usageLimits?: ServerProviderUsageLimits | undefined; }, >( - lastKnown: Ref.Ref, + lastKnown: Ref.Ref, snapshot: Snapshot, ): Effect.Effect { return Effect.gen(function* () { @@ -100,7 +157,11 @@ export function retainSnapshotUsageLimits< const { usageLimits: _usageLimits, ...withoutUsage } = snapshot; return withoutUsage as Snapshot; } - const usageLimits = yield* retainUsageLimits(lastKnown, snapshot.usageLimits); + const usageLimits = yield* retainUsageLimitsForAccount( + lastKnown, + authenticatedUsageIdentity(snapshot.auth), + snapshot.usageLimits, + ); if (usageLimits === snapshot.usageLimits) return snapshot; return usageLimits ? { ...snapshot, usageLimits } : snapshot; }); diff --git a/docs/user/usage.md b/docs/user/usage.md index a82bd83c63..924431d66d 100644 --- a/docs/user/usage.md +++ b/docs/user/usage.md @@ -57,7 +57,9 @@ not report that window. Refresh to update the readings. During native Claude sessions, account-wide quota reports update immediately. Model-specific weekly limits reconcile with the signed-in account after about a minute; provider throttling can delay them. -Pylon keeps the last reading when it cannot verify the account. +Pylon keeps a recent reading through a failed quota check when it can still verify the same +account. It hides that reading while account status is unknown, after sign-out, or after a different +account signs in. Antigravity Google-account limits belong to the account signed in to that Pylon provider instance. Settings and Limits show the model groups reported by Google. The thread composer selects the group From 22d296440460b71759bc208ccc34f7a328591c3e Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:22:17 -0600 Subject: [PATCH 034/104] Sync linked PR changes and settle configured threads on events (#834) --- .../PullRequestSyncReactor.test.ts | 164 ++++++++++++++- .../orchestration/PullRequestSyncReactor.ts | 94 +++++---- .../ThreadSettlementReactor.test.ts | 199 +++++++++++++++++- .../orchestration/ThreadSettlementReactor.ts | 49 ++++- .../pullRequest/PullRequestService.test.ts | 181 +++++++++++++--- .../src/pullRequest/PullRequestService.ts | 28 ++- apps/server/src/ws.ts | 2 +- .../src/state/pullRequests.test.ts | 103 +++++++++ 8 files changed, 719 insertions(+), 101 deletions(-) diff --git a/apps/server/src/orchestration/PullRequestSyncReactor.test.ts b/apps/server/src/orchestration/PullRequestSyncReactor.test.ts index 414f76f324..1406eae239 100644 --- a/apps/server/src/orchestration/PullRequestSyncReactor.test.ts +++ b/apps/server/src/orchestration/PullRequestSyncReactor.test.ts @@ -1,9 +1,11 @@ import { + EventId, ProjectId, ProviderInstanceId, PullRequestOperationError, ThreadId, type OrchestrationCommand, + type OrchestrationEvent, type OrchestrationProjectShell, type OrchestrationShellSnapshot, type OrchestrationThreadShell, @@ -18,6 +20,7 @@ import * as Crypto from "effect/Crypto"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import * as PubSub from "effect/PubSub"; import * as Queue from "effect/Queue"; import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; @@ -31,6 +34,7 @@ import { } from "./Services/OrchestrationEngine.ts"; import { ProjectionSnapshotQuery } from "./Services/ProjectionSnapshotQuery.ts"; import * as PullRequestSyncReactor from "./PullRequestSyncReactor.ts"; +import { resolveAutoSettlementAt } from "./ThreadSettlementPolicy.ts"; const NOW = "2026-08-28T12:00:00.000Z"; const PROJECT_ID = ProjectId.make("sync-project"); @@ -152,6 +156,7 @@ function makeSummary( } interface HarnessOptions { + readonly onDispatch?: (command: SyncCommand | LinkCommand) => Effect.Effect; readonly invalidate?: PullRequestService["Service"]["invalidate"]; readonly snapshot: OrchestrationShellSnapshot; readonly summary?: ( @@ -165,6 +170,7 @@ interface HarnessOptions { const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: HarnessOptions) { const activation = yield* Deferred.make(); const snapshots = yield* Ref.make(options.snapshot); + const events = yield* PubSub.unbounded(); const snapshotReads = yield* Queue.unbounded(); const syncCommands = yield* Ref.make>([]); const linkCommands = yield* Ref.make>([]); @@ -188,11 +194,13 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: const dispatch: OrchestrationEngineShape["dispatch"] = (command) => { if (command.type === "thread.pull-request-link.sync") { return Ref.update(syncCommands, (recorded) => [...recorded, command]).pipe( + Effect.andThen(options.onDispatch?.(command) ?? Effect.void), Effect.as({ sequence: 1 }), ); } if (command.type === "thread.pull-request.link") { return Ref.update(linkCommands, (recorded) => [...recorded, command]).pipe( + Effect.andThen(options.onDispatch?.(command) ?? Effect.void), Effect.as({ sequence: 1 }), ); } @@ -213,6 +221,9 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: readEvents: () => Stream.empty, dispatch, streamDomainEvents: Stream.empty, + subscribeDomainEvents: PubSub.subscribe(events).pipe( + Effect.map((subscription) => Stream.fromSubscription(subscription)), + ), latestSequence: Effect.succeed(0), }), Layer.succeed(ServerActivation, Deferred.await(activation)), @@ -220,6 +231,7 @@ const makeHarness = Effect.fn("makePullRequestSyncHarness")(function* (options: ); return { + events, activation, snapshots, snapshotReads, @@ -274,6 +286,42 @@ function applySync( } describe("PullRequestSyncReactor", () => { + it.effect("syncs a newly linked merged PR without waiting for the periodic sweep", () => + Effect.scoped( + Effect.gen(function* () { + yield* TestClock.setTime(Date.parse(NOW)); + const fixture = yield* makeHarness({ + snapshot: makeSnapshot([makeThread("one")]), + summary: (input) => + Effect.succeed(makeSummary(input, { state: "merged", mergedAt: NOW })), + }); + yield* Effect.gen(function* () { + const reactor = yield* startAndSweep(fixture); + const link = makeLink(42); + yield* Ref.set( + fixture.snapshots, + makeSnapshot([makeThread("one", { pullRequests: [link] })]), + ); + yield* PubSub.publish(fixture.events, { + type: "thread.pull-request-linked", + sequence: 2, + eventId: EventId.make("linked"), + aggregateKind: "thread", + aggregateId: ThreadId.make("one"), + occurredAt: NOW, + commandId: null, + causationEventId: null, + correlationId: null, + metadata: {}, + payload: { threadId: ThreadId.make("one"), link, updatedAt: NOW }, + }); + yield* Queue.take(fixture.snapshotReads); + yield* reactor.drain; + assert.strictEqual((yield* Ref.get(fixture.syncCommands))[0]?.snapshot.state, "merged"); + }).pipe(Effect.provide(fixture.layer)); + }), + ), + ); it.effect("retries a failed stack read after the summary becomes terminal", () => Effect.scoped( Effect.gen(function* () { @@ -303,6 +351,7 @@ describe("PullRequestSyncReactor", () => { yield* Effect.gen(function* () { const reactor = yield* startAndSweep(fixture); const commands = yield* Ref.get(fixture.syncCommands); + assert.deepStrictEqual(commands, []); yield* Ref.update(fixture.snapshots, (snapshot) => applySync(snapshot, commands)); yield* sweepAgain(fixture, reactor); assert.strictEqual(attempts, 2); @@ -315,6 +364,85 @@ describe("PullRequestSyncReactor", () => { ), ); + it.effect("retries a failed sibling link before publishing a terminal snapshot", () => + Effect.scoped( + Effect.gen(function* () { + yield* TestClock.setTime(Date.parse(NOW)); + let failSibling = true; + const fixture = yield* makeHarness({ + snapshot: makeSnapshot([ + makeThread("one", { pullRequests: [makeLink(7, { state: "closed" })] }), + ]), + summary: (input) => + Effect.succeed(makeSummary(input, { state: "merged", mergedAt: NOW })), + stack: () => + Effect.succeed({ + id: "stack", + number: 7, + url: "https://github.com/owner/repository/stacks/7", + base: "main", + layers: [ + { number: 7, headBranch: "feature", state: "merged" }, + { number: 8, headBranch: "sibling", state: "open" }, + ], + }), + onDispatch: (command) => + command.type === "thread.pull-request.link" && failSibling + ? Effect.die("temporary link failure") + : Effect.void, + }); + yield* Effect.gen(function* () { + const reactor = yield* startAndSweep(fixture); + assert.deepStrictEqual(yield* Ref.get(fixture.syncCommands), []); + failSibling = false; + yield* sweepAgain(fixture, reactor); + assert.strictEqual((yield* Ref.get(fixture.syncCommands))[0]?.snapshot.state, "merged"); + assert.strictEqual((yield* Ref.get(fixture.linkCommands)).at(-1)?.number, 8); + }).pipe(Effect.provide(fixture.layer)); + }), + ), + ); + + it.effect("concurrent stack reads persist a shared sibling once before syncing both roots", () => + Effect.scoped( + Effect.gen(function* () { + const readsReady = yield* Deferred.make(); + let reads = 0; + let linked = false; + const fixture = yield* makeHarness({ + snapshot: makeSnapshot([makeThread("one", { pullRequests: [makeLink(7), makeLink(8)] })]), + stack: () => + Effect.gen(function* () { + if (++reads === 2) yield* Deferred.succeed(readsReady, undefined); + yield* Deferred.await(readsReady); + return { + id: "stack", + number: 7, + url: "https://github.com/owner/repository/stacks/7", + base: "main", + layers: [{ number: 9, headBranch: "sibling", state: "open" as const }], + }; + }), + onDispatch: (command) => + Effect.gen(function* () { + if (command.type === "thread.pull-request.link") { + yield* Effect.yieldNow; + assert.strictEqual(linked, false); + linked = true; + } else { + assert.strictEqual(linked, true); + } + }), + }); + yield* Effect.gen(function* () { + yield* startAndSweep(fixture); + assert.strictEqual((yield* Ref.get(fixture.linkCommands)).length, 1); + assert.strictEqual((yield* Ref.get(fixture.syncCommands)).length, 2); + }).pipe(Effect.provide(fixture.layer)); + }), + ), + ); + it.effect("explicit refresh reads a changed stack even when its PR summary is unchanged", () => Effect.scoped( Effect.gen(function* () { @@ -631,20 +759,38 @@ describe("PullRequestSyncReactor", () => { base: "main", layers: [ { number: 41, headBranch: "layer-1", state: "merged" }, - { number: 42, headBranch: "layer-2", state: "open" }, + { number: 42, headBranch: "layer-2", state: "merged" }, { number: 43, headBranch: "layer-3", state: "open" }, ], }; + let thread = makeThread("one", { + pullRequests: [ + makeLink(42, { state: "open" }), + makeLink(41, { state: "merged" }, { source: "stack-dismissed" }), + ], + }); const fixture = yield* makeHarness({ - snapshot: makeSnapshot([ - makeThread("one", { - pullRequests: [ - makeLink(42), - makeLink(41, { state: "merged" }, { source: "stack-dismissed" }), - ], - }), - ]), + snapshot: makeSnapshot([thread]), + summary: (input) => + Effect.succeed(makeSummary(input, { state: "merged", mergedAt: NOW })), stack: () => Effect.succeed(stack), + onDispatch: (command) => + Effect.sync(() => { + thread = + command.type === "thread.pull-request.link" + ? { ...thread, pullRequests: [...thread.pullRequests, makeLink(command.number)] } + : applySync(makeSnapshot([thread]), [command]).threads[0]!; + // Every projected event may wake settlement, including the terminal root update. + assert.isNull( + resolveAutoSettlementAt({ + thread, + pullRequest: null, + now: NOW, + autoSettleAfterDays: null, + autoSettleOnMerge: true, + }), + ); + }), }); yield* Effect.gen(function* () { diff --git a/apps/server/src/orchestration/PullRequestSyncReactor.ts b/apps/server/src/orchestration/PullRequestSyncReactor.ts index eb74dc3486..2298530210 100644 --- a/apps/server/src/orchestration/PullRequestSyncReactor.ts +++ b/apps/server/src/orchestration/PullRequestSyncReactor.ts @@ -22,6 +22,8 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schedule from "effect/Schedule"; import type * as Scope from "effect/Scope"; +import * as Semaphore from "effect/Semaphore"; +import * as Stream from "effect/Stream"; import * as PullRequestService from "../pullRequest/PullRequestService.ts"; import { forkParked } from "../serverActivation.ts"; @@ -149,7 +151,7 @@ export const make = Effect.gen(function* () { (cause: Cause.Cause): Effect.Effect => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) : Effect.logWarning(message, fields); - const sweep = Effect.fn("PullRequestSyncReactor.sweep")(function* () { + const sweep = Effect.fn("PullRequestSyncReactor.sweep")(function* (requestedKey?: string) { const snapshot = yield* snapshots.getShellSnapshot(); const now = yield* DateTime.now; const nowMs = DateTime.toEpochMillis(now); @@ -173,6 +175,7 @@ export const make = Effect.gen(function* () { // Layers auto-linked this sweep, so two links of one thread that share a // stack do not both try to add the same sibling. const linkedThisSweep = new Set(); + const persistence = yield* Semaphore.make(1); const syncEntry = Effect.fn("PullRequestSyncReactor.syncEntry")(function* ( entry: LinkEntry, @@ -185,21 +188,8 @@ export const make = Effect.gen(function* () { link.snapshot === null || !snapshotFieldsEqual(link.snapshot, fields) || !stacksEqual(link.stack, nextStack); - if (changed) { - const uuid = yield* crypto.randomUUIDv4; - yield* engine.dispatch({ - type: "thread.pull-request-link.sync", - commandId: CommandId.make(`server:pr-sync:${thread.id}:${uuid}`), - threadId: thread.id, - host: link.host, - repository: link.repository, - number: link.number, - snapshot: { ...fields, syncedAt: nowIso }, - stack: nextStack, - }); - } - if (fetchedStack === null || fetchedStack.stack === null) return; - for (const layer of fetchedStack.stack.layers) { + // Persist discovered siblings before a terminal snapshot can trigger settlement. + for (const layer of fetchedStack?.stack?.layers ?? []) { const layerKey = { host: link.host, repository: link.repository, number: layer.number }; const dedupeKey = `${thread.id}:${threadPullRequestKeyOf(layerKey)}`; if (linkedThisSweep.has(dedupeKey)) continue; @@ -211,25 +201,29 @@ export const make = Effect.gen(function* () { } const url = siblingPullRequestUrl(link.url, layer.number); if (url === null) continue; + const uuid = yield* crypto.randomUUIDv4; + yield* engine.dispatch({ + type: "thread.pull-request.link", + commandId: CommandId.make(`server:pr-stack-link:${thread.id}:${uuid}`), + threadId: thread.id, + ...layerKey, + url, + source: "stack", + }); linkedThisSweep.add(dedupeKey); + } + if (changed) { const uuid = yield* crypto.randomUUIDv4; - yield* engine - .dispatch({ - type: "thread.pull-request.link", - commandId: CommandId.make(`server:pr-stack-link:${thread.id}:${uuid}`), - threadId: thread.id, - ...layerKey, - url, - source: "stack", - }) - .pipe( - Effect.catchCause( - logSkipped("pull request stack layer link skipped", { - threadId: thread.id, - number: layer.number, - }), - ), - ); + yield* engine.dispatch({ + type: "thread.pull-request-link.sync", + commandId: CommandId.make(`server:pr-sync:${thread.id}:${uuid}`), + threadId: thread.id, + host: link.host, + repository: link.repository, + number: link.number, + snapshot: { ...fields, syncedAt: nowIso }, + stack: nextStack, + }); } }); @@ -270,8 +264,11 @@ export const make = Effect.gen(function* () { ) : null; if (needsStack) { - if (fetchedStack === null) retryStacks.add(key); - else retryStacks.delete(key); + if (fetchedStack === null) { + retryStacks.add(key); + return; + } + retryStacks.delete(key); } // The host answered, so the cadence clock ticks even if a dispatch below is rejected. lastSyncedAt.set(key, nowMs); @@ -281,9 +278,13 @@ export const make = Effect.gen(function* () { entries, (entry) => syncEntry(entry, fields, fetchedStack).pipe( - Effect.catchCause( - logSkipped("pull request sync skipped", { threadId: entry.thread.id, key }), - ), + persistence.withPermits(1), + Effect.catchCause((cause) => { + if (!Cause.hasInterruptsOnly(cause)) retryStacks.add(key); + return logSkipped("pull request sync skipped", { threadId: entry.thread.id, key })( + cause, + ); + }), ), { discard: true }, ); @@ -292,7 +293,7 @@ export const make = Effect.gen(function* () { yield* Effect.forEach( groups, ([key, entries]) => - isDue(key, entries, nowMs) + (requestedKey === undefined || requestedKey === key) && isDue(key, entries, nowMs) ? syncGroup(key, entries).pipe( Effect.catchCause(logSkipped("pull request sync skipped", { key })), ) @@ -303,13 +304,19 @@ export const make = Effect.gen(function* () { ); }); - const worker = yield* makeDrainableWorker(() => - sweep().pipe(Effect.catchCause(logSkipped("pull request sync sweep failed", {}))), + const worker = yield* makeDrainableWorker((key: string | undefined) => + sweep(key).pipe(Effect.catchCause(logSkipped("pull request sync sweep failed", {}))), ); const start: PullRequestSyncReactor["Service"]["start"] = Effect.fn( "PullRequestSyncReactor.start", )(function* () { + const events = yield* engine.subscribeDomainEvents; + yield* forkParked( + Stream.runForEach(events, (event) => + event.type === "thread.pull-request-linked" ? requestSync(event.payload.link) : Effect.void, + ), + ); yield* forkParked( Effect.gen(function* () { yield* worker.enqueue(undefined); @@ -320,8 +327,9 @@ export const make = Effect.gen(function* () { const requestSync: PullRequestSyncReactor["Service"]["requestSync"] = (key) => Effect.suspend(() => { - requested.set(threadPullRequestKeyOf(key), ++requestGeneration); - return worker.enqueue(undefined); + const syncKey = threadPullRequestKeyOf(key); + requested.set(syncKey, ++requestGeneration); + return worker.enqueue(syncKey); }); return { start, drain: worker.drain, requestSync } satisfies PullRequestSyncReactor["Service"]; diff --git a/apps/server/src/orchestration/ThreadSettlementReactor.test.ts b/apps/server/src/orchestration/ThreadSettlementReactor.test.ts index 03519e0fe3..99a02d8e5b 100644 --- a/apps/server/src/orchestration/ThreadSettlementReactor.test.ts +++ b/apps/server/src/orchestration/ThreadSettlementReactor.test.ts @@ -1,10 +1,12 @@ import { DEFAULT_SERVER_SETTINGS, + EventId, ProjectId, ProviderInstanceId, PullRequestOperationError, ThreadId, type OrchestrationCommand, + type OrchestrationEvent, type OrchestrationProjectShell, type OrchestrationShellSnapshot, type OrchestrationThreadShell, @@ -171,6 +173,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: const settingsReads = yield* Queue.unbounded(); const settingsChanges = yield* PubSub.unbounded(); const mergedPullRequests = yield* PubSub.unbounded(); + const domainEvents = yield* PubSub.unbounded(); const commands = yield* Ref.make>([]); const branchCalls = yield* Ref.make< ReadonlyArray<{ readonly cwd: string; readonly branch: string }> @@ -261,6 +264,9 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: readEvents: () => Stream.empty, dispatch, streamDomainEvents: Stream.empty, + subscribeDomainEvents: PubSub.subscribe(domainEvents).pipe( + Effect.map((subscription) => Stream.fromSubscription(subscription)), + ), latestSequence: Effect.succeed(0), }), Layer.succeed(ServerSettingsService, serverSettings), @@ -283,6 +289,7 @@ const makeHarness = Effect.fn("makeThreadSettlementHarness")(function* (options: summaryRecovery, invalidatedCwds, updateSettings, + publishEvent: (event: OrchestrationEvent) => PubSub.publish(domainEvents, event), publishMerge: PubSub.publish(mergedPullRequests, { projectId: PROJECT_ID, repository: "owner/repository", @@ -306,12 +313,12 @@ const startHarness = Effect.fn("startThreadSettlementHarness")(function* ( describe("ThreadSettlementReactor", () => { it.effect( - "settles all-terminal links from snapshots and keeps open or unsynced links active", + "settles synced terminal links immediately while open, unsynced, or running threads wait", () => Effect.scoped( Effect.gen(function* () { yield* TestClock.setTime(Date.parse(NOW)); - const link = (number: number, state: "open" | "merged" | null) => ({ + const link = (number: number, state: "open" | "closed" | "merged" | null) => ({ host: "example.test", repository: "owner/repository", number, @@ -331,14 +338,30 @@ describe("ThreadSettlementReactor", () => { updatedAt: NOW, syncedAt: NOW, mergedAt: state === "merged" ? NOW : null, + closedAt: state === "closed" ? NOW : null, }, }); + const runningSession = { + threadId: ThreadId.make("running"), + status: "running" as const, + providerName: "Codex", + runtimeMode: "full-access" as const, + activeTurnId: null, + lastError: null, + updatedAt: NOW, + }; + const threads = [ + makeThread("merged", { pullRequests: [link(1, "open"), link(2, "merged")] }), + makeThread("closed", { pullRequests: [link(1, "open")] }), + makeThread("open", { pullRequests: [link(1, "open"), link(2, "open")] }), + makeThread("unsynced", { pullRequests: [link(1, "open"), link(2, null)] }), + makeThread("running", { + pullRequests: [link(1, "open")], + session: runningSession, + }), + ]; const fixture = yield* makeHarness({ - snapshot: makeSnapshot([ - makeThread("merged", { pullRequests: [link(1, "merged"), link(2, "merged")] }), - makeThread("open", { pullRequests: [link(1, "merged"), link(2, "open")] }), - makeThread("unsynced", { pullRequests: [link(1, "merged"), link(2, null)] }), - ]), + snapshot: makeSnapshot(threads), settings: { ...DEFAULT_SERVER_SETTINGS, sidebarAutoSettleOnMerge: true }, branchPullRequest: () => Effect.die("linked threads must not query the branch"), pullRequestSummary: () => Effect.die("linked threads must use their snapshots"), @@ -346,9 +369,66 @@ describe("ThreadSettlementReactor", () => { yield* Effect.gen(function* () { const reactor = yield* ThreadSettlementReactor.ThreadSettlementReactor; yield* startHarness(reactor, fixture.activation, fixture.snapshotReads); + assert.deepStrictEqual(yield* Ref.get(fixture.commands), []); + const eventBase = { + sequence: 2, + eventId: EventId.make("pull-request-synced"), + aggregateKind: "thread" as const, + occurredAt: NOW, + commandId: null, + causationEventId: null, + correlationId: null, + metadata: {}, + }; + for (const thread of threads) { + const terminalLink = link(1, thread.id === "closed" ? "closed" : "merged"); + yield* Ref.update(fixture.snapshots, (snapshot) => ({ + ...snapshot, + threads: snapshot.threads.map((current) => + current.id === thread.id + ? { ...current, pullRequests: [terminalLink, ...current.pullRequests.slice(1)] } + : current, + ), + })); + yield* fixture.publishEvent({ + ...eventBase, + type: "thread.pull-request-synced", + aggregateId: thread.id, + payload: { + threadId: thread.id, + host: terminalLink.host, + repository: terminalLink.repository, + number: terminalLink.number, + snapshot: terminalLink.snapshot!, + stack: null, + updatedAt: NOW, + }, + }); + yield* Queue.take(fixture.snapshotReads); + yield* reactor.drain; + } + assert.deepStrictEqual( + (yield* Ref.get(fixture.commands)).map(({ threadId }) => threadId), + [ThreadId.make("merged"), ThreadId.make("closed")], + ); + const readySession = { ...runningSession, status: "ready" as const }; + yield* Ref.update(fixture.snapshots, (snapshot) => ({ + ...snapshot, + threads: snapshot.threads.map((thread) => + thread.id === readySession.threadId ? { ...thread, session: readySession } : thread, + ), + })); + yield* fixture.publishEvent({ + ...eventBase, + type: "thread.session-set", + aggregateId: readySession.threadId, + payload: { threadId: readySession.threadId, session: readySession }, + }); + yield* Queue.take(fixture.snapshotReads); + yield* reactor.drain; assert.deepStrictEqual( (yield* Ref.get(fixture.commands)).map(({ threadId }) => threadId), - [ThreadId.make("merged")], + [ThreadId.make("merged"), ThreadId.make("closed"), ThreadId.make("running")], ); assert.deepStrictEqual(yield* Ref.get(fixture.branchCalls), []); assert.deepStrictEqual(yield* Ref.get(fixture.summaryCalls), []); @@ -357,6 +437,109 @@ describe("ThreadSettlementReactor", () => { ), ); + it.effect("rechecks only the affected thread after link and unlink events", () => + Effect.scoped( + Effect.gen(function* () { + yield* TestClock.setTime(Date.parse(NOW)); + const link = (number: number, state: "open" | "merged") => ({ + host: "example.test", + repository: "owner/repository", + number, + url: `https://example.test/owner/repository/pull/${number}`, + source: "manual" as const, + linkedAt: NOW, + stack: null, + snapshot: { + state, + title: "Review", + headBranch: "feature", + baseBranch: "main", + isDraft: false, + updatedAt: NOW, + syncedAt: NOW, + mergedAt: state === "merged" ? NOW : null, + closedAt: null, + }, + }); + const merged = link(1, "merged"); + const open = link(2, "open"); + const fixture = yield* makeHarness({ + snapshot: makeSnapshot([ + makeThread("new-link"), + makeThread("removed-link", { pullRequests: [open, merged] }), + ]), + settings: { + ...DEFAULT_SERVER_SETTINGS, + sidebarAutoSettleOnMerge: true, + sidebarAutoSettleAfterDays: null, + }, + branchPullRequest: () => Effect.die("linked event must use the projected snapshot"), + pullRequestSummary: () => Effect.die("linked event must not query a provider"), + }); + yield* Effect.gen(function* () { + const reactor = yield* ThreadSettlementReactor.ThreadSettlementReactor; + yield* startHarness(reactor, fixture.activation, fixture.snapshotReads); + assert.deepStrictEqual(yield* Ref.get(fixture.commands), []); + const eventBase = { + sequence: 2, + eventId: EventId.make("link-change"), + aggregateKind: "thread" as const, + occurredAt: NOW, + commandId: null, + causationEventId: null, + correlationId: null, + metadata: {}, + }; + + yield* Ref.update(fixture.snapshots, (snapshot) => ({ + ...snapshot, + threads: snapshot.threads.map((thread) => + thread.id === ThreadId.make("new-link") + ? { ...thread, pullRequests: [merged] } + : thread, + ), + })); + yield* fixture.publishEvent({ + ...eventBase, + type: "thread.pull-request-linked", + aggregateId: ThreadId.make("new-link"), + payload: { threadId: ThreadId.make("new-link"), link: merged, updatedAt: NOW }, + }); + yield* Queue.take(fixture.snapshotReads); + yield* reactor.drain; + + yield* Ref.update(fixture.snapshots, (snapshot) => ({ + ...snapshot, + threads: snapshot.threads.map((thread) => + thread.id === ThreadId.make("removed-link") + ? { ...thread, pullRequests: [merged] } + : thread, + ), + })); + yield* fixture.publishEvent({ + ...eventBase, + type: "thread.pull-request-unlinked", + aggregateId: ThreadId.make("removed-link"), + payload: { + threadId: ThreadId.make("removed-link"), + host: open.host, + repository: open.repository, + number: open.number, + updatedAt: NOW, + }, + }); + yield* Queue.take(fixture.snapshotReads); + yield* reactor.drain; + assert.deepStrictEqual( + (yield* Ref.get(fixture.commands)).map(({ threadId }) => threadId), + [ThreadId.make("new-link"), ThreadId.make("removed-link")], + ); + assert.deepStrictEqual(yield* Ref.get(fixture.summaryCalls), []); + }).pipe(Effect.provide(fixture.layer)); + }), + ), + ); + it("distinguishes a project that inherits the threshold from one that disables it", () => { const inherits = ThreadSettlementReactor.autoSettlementSettingsKey({ ...DEFAULT_SERVER_SETTINGS, diff --git a/apps/server/src/orchestration/ThreadSettlementReactor.ts b/apps/server/src/orchestration/ThreadSettlementReactor.ts index 193759d9c4..ff6f995df4 100644 --- a/apps/server/src/orchestration/ThreadSettlementReactor.ts +++ b/apps/server/src/orchestration/ThreadSettlementReactor.ts @@ -1,4 +1,9 @@ -import { CommandId, type ServerSettings as ServerSettingsValue } from "@t3tools/contracts"; +import { + CommandId, + type OrchestrationEvent, + type ServerSettings as ServerSettingsValue, + type ThreadId, +} from "@t3tools/contracts"; import { resolveProjectSettings } from "@t3tools/shared/projectSettings"; import { makeDrainableWorker } from "@t3tools/shared/DrainableWorker"; import * as Cause from "effect/Cause"; @@ -84,6 +89,7 @@ export const make = Effect.gen(function* () { const sweep = Effect.fn("ThreadSettlementReactor.sweep")(function* ( mergedPullRequest: PullRequestService.PullRequestMergeEvent | null, + threadId?: ThreadId, ) { const settings = yield* settingsService.getSettings; if (!autoSettlementConfigured(settings)) { @@ -94,7 +100,11 @@ export const make = Effect.gen(function* () { const projects = new Map(snapshot.projects.map((project) => [project.id, project])); // A merge rechecks all candidates, including branches that discovery has // not linked yet. Those lookups can still have cached the PR as open. - const candidates = snapshot.threads.filter((thread) => isAutoSettlementCandidate(thread, now)); + const candidates = snapshot.threads.filter( + (thread) => + (threadId === undefined || thread.id === threadId) && + isAutoSettlementCandidate(thread, now), + ); // Return the thread when it still needs a pull request decision. A rejected // dispatch skips it for this snapshot instead of retrying through a lookup. @@ -308,8 +318,11 @@ export const make = Effect.gen(function* () { ); }); - const runSweep = (mergedPullRequest: PullRequestService.PullRequestMergeEvent | null) => - sweep(mergedPullRequest).pipe( + const runSweep = ( + mergedPullRequest: PullRequestService.PullRequestMergeEvent | null, + threadId?: ThreadId, + ) => + sweep(mergedPullRequest, threadId).pipe( Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) @@ -318,13 +331,36 @@ export const make = Effect.gen(function* () { }), ), ); - const worker = yield* makeDrainableWorker(() => runSweep(null)); + const worker = yield* makeDrainableWorker((threadId: ThreadId | undefined) => + runSweep(null, threadId), + ); + + const processEvent = (event: OrchestrationEvent) => { + switch (event.type) { + case "thread.pull-request-linked": + case "thread.pull-request-synced": + case "thread.pull-request-unlinked": + // Merge notifications can arrive before the linked snapshot is projected. + // Recheck the persisted state so terminal links settle without the timer. + return worker.enqueue(event.payload.threadId); + case "thread.session-set": + if ( + event.payload.session.status !== "running" && + event.payload.session.status !== "starting" + ) { + return worker.enqueue(event.payload.threadId); + } + break; + } + return Effect.void; + }; const start: ThreadSettlementReactor["Service"]["start"] = Effect.fn( "ThreadSettlementReactor.start", )(function* () { const settingsChanges = yield* settingsService.subscribeChanges; const mergedPullRequests = yield* pullRequests.subscribeMerges; + const events = yield* engine.subscribeDomainEvents; const initialSettings = yield* settingsService.getSettings.pipe(Effect.orDie); let lastSettlementSettings = autoSettlementSettingsKey(initialSettings); yield* forkParked( @@ -343,7 +379,8 @@ export const make = Effect.gen(function* () { return worker.enqueue(undefined); }), ); - yield* forkParked(Stream.runForEach(mergedPullRequests, runSweep)); + yield* forkParked(Stream.runForEach(mergedPullRequests, (event) => runSweep(event))); + yield* forkParked(Stream.runForEach(events, processEvent)); }); return { start, drain: worker.drain } satisfies ThreadSettlementReactor["Service"]; diff --git a/apps/server/src/pullRequest/PullRequestService.test.ts b/apps/server/src/pullRequest/PullRequestService.test.ts index a4f866b59a..e84703f241 100644 --- a/apps/server/src/pullRequest/PullRequestService.test.ts +++ b/apps/server/src/pullRequest/PullRequestService.test.ts @@ -1081,8 +1081,13 @@ it.effect("publishes a merge for immediate settlement only after host confirmati // Queueing succeeds while the host still reports an open PR. yield* service.runAction({ ...reference, action: "merge" }); + const queuedRefresh = Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)); confirmationFails = true; yield* service.runAction({ ...reference, action: "merge" }); + assert.isAbove( + Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)), + queuedRefresh, + ); confirmationFails = false; state = "merged"; yield* TestClock.setTime(Date.parse(mergedAt)); @@ -1101,6 +1106,53 @@ it.effect("publishes a merge for immediate settlement only after host confirmati ), ); +it.effect("refreshes every reader before a queued merge confirmation finishes", () => + Effect.scoped( + Effect.gen(function* () { + const confirmationStarted = yield* Deferred.make(); + const confirm = yield* Deferred.make(); + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakeProvider("github", { + getChangeRequestSummary: () => + Effect.gen(function* () { + yield* Deferred.succeed(confirmationStarted, undefined); + yield* Deferred.await(confirm); + return changeRequest(1, "2026-09-16T00:00:00.000Z"); + }), + }), + ], + }); + const merges = yield* service.subscribeMerges; + const observedMerge = yield* Stream.runHead(merges).pipe( + Effect.forkChild({ startImmediately: true }), + ); + const readers = yield* Effect.forEach([0, 1], () => + Stream.runHead(service.subscribeRefreshes).pipe( + Effect.forkChild({ startImmediately: true }), + ), + ); + const action = yield* service + .runAction({ ...reference, action: "merge" }) + .pipe(Effect.forkChild({ startImmediately: true })); + yield* Deferred.await(confirmationStarted); + const revisions = yield* Effect.forEach(readers, (reader) => + Fiber.join(reader).pipe(Effect.map(Option.getOrThrow)), + ); + assert.isAbove(revisions[0]!, 0); + assert.strictEqual(revisions[0], revisions[1]); + assert.isUndefined(action.pollUnsafe()); + yield* Deferred.succeed(confirm, undefined); + yield* Fiber.join(action); + assert.isUndefined(observedMerge.pollUnsafe()); + }), + ), +); + it.effect("refuses an action this viewer may not take, and says what access it takes", () => Effect.gen(function* () { let ran: string | null = null; @@ -2254,10 +2306,16 @@ it.effect("invalidates the cached activity after reacting, like the other mutati ], }); + yield* service.refreshAfterTurn(reference.projectId); + const previousRefresh = Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)); yield* service.activity(reference); assert.strictEqual(activityCalls, 1); yield* service.setReaction({ ...reference, content: "heart", reacted: true }); + assert.isAbove( + Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)), + previousRefresh, + ); yield* service.activity(reference); assert.strictEqual(activityCalls, 2); @@ -3192,31 +3250,106 @@ it.effect("explicit and turn invalidations make the next listing ask the host ag }), ); -it.effect("a mutation makes the next listing ask the host again, with no client asking", () => - Effect.gen(function* () { - let hostCalls = 0; - const service = yield* makeService({ - projects: [project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" })], - providers: [ - fakeProvider("github", { - listChangeRequests: () => { - hostCalls += 1; - return Effect.succeed({ items: [], truncated: false, continues: false }); - }, - }), - ], - }); +it.effect("close and reopen notify subscribed readers after invalidating their cached state", () => + Effect.scoped( + Effect.gen(function* () { + let hostCalls = 0; + let state: "open" | "closed" = "open"; + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakeProvider("github", { + getChangeRequestSummary: () => + Effect.succeed({ ...changeRequest(1, "2026-09-16T00:00:00.000Z"), state }), + runAction: (input) => + Effect.sync(() => { + state = input.action === "close" ? "closed" : "open"; + }), + listChangeRequests: () => { + hostCalls += 1; + return Effect.succeed({ items: [], truncated: false, continues: false }); + }, + }), + ], + }); - yield* service.list({ state: "open" }); - yield* service.runAction({ - projectId: "p1" as ProjectId, - repository: "acme/web", - number: 1, - action: "close", - }); - yield* service.list({ state: "open" }); - assert.strictEqual(hostCalls, 2); - }), + yield* service.refreshAfterTurn(reference.projectId); + yield* service.list({ state: "open" }); + assert.strictEqual((yield* service.summary(reference)).state, "open"); + for (const action of ["close", "reopen"] as const) { + const refreshed = yield* service.subscribeRefreshes.pipe( + Stream.drop(1), + Stream.take(1), + Stream.mapEffect(() => + Effect.gen(function* () { + yield* service.list({ state: "open" }); + return yield* service.summary(reference); + }), + ), + Stream.runHead, + Effect.forkChild({ startImmediately: true }), + ); + yield* service.runAction({ ...reference, action }); + assert.strictEqual( + Option.getOrThrow(yield* Fiber.join(refreshed)).state, + action === "close" ? "closed" : "open", + ); + } + assert.strictEqual(hostCalls, 3); + }), + ), +); + +it.effect("explicit invalidation refreshes origin readers after a routed host mutation", () => + Effect.scoped( + Effect.gen(function* () { + let state: "open" | "closed" = "open"; + const reference = { projectId: "p1" as ProjectId, repository: "acme/web", number: 1 }; + const service = yield* makeService({ + projects: [ + project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web" }), + ], + providers: [ + fakeProvider("github", { + getChangeRequestSummary: () => + Effect.succeed({ ...changeRequest(1, "2026-09-16T00:00:00.000Z"), state }), + }), + ], + }); + yield* service.refreshAfterTurn(reference.projectId); + let revision = Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)); + // The sync reactor invalidates before reading; it must not notify itself again. + yield* service.invalidate({ reference }); + assert.strictEqual( + Option.getOrThrow(yield* Stream.runHead(service.subscribeRefreshes)), + revision, + ); + assert.strictEqual((yield* service.summary(reference)).state, "open"); + + for (const nextState of ["closed", "open"] as const) { + const refreshed = yield* service.subscribeRefreshes.pipe( + Stream.drop(1), + Stream.take(1), + Stream.mapEffect((nextRevision) => + service + .summary(reference) + .pipe(Effect.map((summary) => ({ revision: nextRevision, state: summary.state }))), + ), + Stream.runHead, + Effect.forkChild({ startImmediately: true }), + ); + state = nextState; + yield* service.invalidate({ reference }, { notifyReaders: true }); + const result = Option.getOrThrow(yield* Fiber.join(refreshed)); + assert.strictEqual(result.state, nextState); + assert.isAbove(result.revision, revision); + revision = result.revision; + } + }), + ), ); it.effect("does not cache a failed listing", () => diff --git a/apps/server/src/pullRequest/PullRequestService.ts b/apps/server/src/pullRequest/PullRequestService.ts index c61ce781a8..ff53821727 100644 --- a/apps/server/src/pullRequest/PullRequestService.ts +++ b/apps/server/src/pullRequest/PullRequestService.ts @@ -214,7 +214,10 @@ export class PullRequestService extends Context.Service< readonly setLabels: ( input: PullRequestLabelChangeInput, ) => Effect.Effect; - readonly invalidate: (input: PullRequestInvalidateInput) => Effect.Effect; + readonly invalidate: ( + input: PullRequestInvalidateInput, + options?: { readonly notifyReaders?: boolean }, + ) => Effect.Effect; } >()("t3/pullRequest/PullRequestService") {} @@ -2732,10 +2735,12 @@ export const make = Effect.gen(function* () { return { stats: [...held, ...result.stats] }; }); - const invalidate: PullRequestService["Service"]["invalidate"] = (input) => { + const invalidate: PullRequestService["Service"]["invalidate"] = Effect.fn( + "PullRequestService.invalidate", + )(function* (input, options) { const reference = input.reference; if (reference !== undefined) { - return canonicalRef(reference).pipe( + yield* canonicalRef(reference).pipe( Effect.flatMap((ref) => readCache .invalidate(refScope(ref)) @@ -2743,12 +2748,15 @@ export const make = Effect.gen(function* () { ), Effect.ignore, ); - } - return Effect.sync(() => { + } else { listingsEpoch = ++epochCounter; viewersByHost.clear(); - }).pipe(Effect.andThen(Cache.invalidateAll(viewerFlights))); - }; + yield* Cache.invalidateAll(viewerFlights); + } + if (options?.notifyReaders) { + yield* SubscriptionRef.set(pullRequestRefreshes, ++epochCounter); + } + }); const refreshAfterTurn: PullRequestService["Service"]["refreshAfterTurn"] = (projectId) => Effect.suspend(() => { @@ -2767,9 +2775,7 @@ export const make = Effect.gen(function* () { .pipe(Effect.andThen(SubscriptionRef.set(pullRequestRefreshes, listingsEpoch))); }); - // A mutation's own client re-reads right after it, and every other client's next read must - // see the action too — so a write forgets the change request it touched and the listings its - // state change reorders, for everyone, without any client asking. + // Invalidate before notifying every client so mounted readers immediately fetch the edit. const invalidatedByMutation = ( method: (input: I) => Effect.Effect, @@ -2787,6 +2793,7 @@ export const make = Effect.gen(function* () { }), ), ); + yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); }); const runActionAndInvalidate: PullRequestService["Service"]["runAction"] = Effect.fn( "PullRequestService.runActionAndInvalidate", @@ -2798,6 +2805,7 @@ export const make = Effect.gen(function* () { ); bumpRefEpoch({ ...ref, repository }); listingsEpoch = ++epochCounter; + yield* SubscriptionRef.set(pullRequestRefreshes, listingsEpoch); if (input.action === "merge") { // A successful merge action can merely enqueue the PR or enable auto-merge. const confirmed = yield* summaryUncached({ ...input, repository }).pipe( diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index 92bdd6e753..b4b4035f5a 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -3124,7 +3124,7 @@ const makeWsRpcLayer = ( [WS_METHODS.pullRequestsInvalidate]: (input) => observeRpcEffect( WS_METHODS.pullRequestsInvalidate, - pullRequests.invalidate(input).pipe( + pullRequests.invalidate(input, { notifyReaders: true }).pipe( // A reader asking for fresh host state also wants the thread badges it feeds to // catch up, including a merged link the sweep would otherwise never revisit. Effect.andThen( diff --git a/packages/client-runtime/src/state/pullRequests.test.ts b/packages/client-runtime/src/state/pullRequests.test.ts index 6187cf2e7b..fe6ea90a59 100644 --- a/packages/client-runtime/src/state/pullRequests.test.ts +++ b/packages/client-runtime/src/state/pullRequests.test.ts @@ -21,6 +21,7 @@ import * as EnvironmentSupervisor from "../connection/supervisor.ts"; import type { WsRpcProtocolClient } from "../rpc/protocol.ts"; import type { RpcSession } from "../rpc/session.ts"; import { + createLinkedPullRequestSummaryAtomFamily, createPullRequestEnvironmentAtoms, createPullRequestStackAtomFamily, } from "./pullRequests.ts"; @@ -134,6 +135,108 @@ it.effect("keeps concurrent diff file reads on different hosts separate", () => ), ); +it.effect("shares close, reopen, and merge with an untouched client's mounted PR readers", () => + Effect.scoped( + Effect.gen(function* () { + const revision = yield* SubscriptionRef.make(0); + let state: "open" | "closed" | "merged" = "open"; + const client = { + [WS_METHODS.pullRequestsSubscribeRefreshes]: () => SubscriptionRef.changes(revision), + [WS_METHODS.pullRequestsSummary]: () => Effect.sync(() => ({ state })), + [WS_METHODS.pullRequestsDetail]: () => Effect.sync(() => ({ state })), + [WS_METHODS.pullRequestsList]: () => + Effect.sync(() => ({ entries: [{ number: 1, state }] })), + [WS_METHODS.pullRequestsRunAction]: (input: { + readonly action: "close" | "reopen" | "merge"; + }) => + Effect.gen(function* () { + state = + input.action === "close" ? "closed" : input.action === "merge" ? "merged" : "open"; + yield* SubscriptionRef.update(revision, (value) => value + 1); + }), + } as unknown as WsRpcProtocolClient; + const writer = yield* makeTestRuntime(client); + const reader = yield* makeTestRuntime(client); + const target = { + environmentId: TARGET.environmentId, + input: { + projectId: ProjectId.make("project-1"), + host: "github.example.com", + repository: "acme/web", + number: 1, + }, + }; + const detail = reader.atoms.detail(target); + const summary = createLinkedPullRequestSummaryAtomFamily( + reader.runtime, + reader.atoms.refreshes, + )(target); + const list = reader.atoms.list({ + environmentId: TARGET.environmentId, + input: { state: "all" }, + }); + const unmountDetail = reader.registry.mount(detail); + const unmountSummary = reader.registry.mount(summary); + const unmountList = reader.registry.mount(list); + yield* Effect.addFinalizer(() => + Effect.sync(() => { + unmountDetail(); + unmountSummary(); + unmountList(); + }), + ); + expect((yield* AtomRegistry.getResult(reader.registry, detail)).state).toBe("open"); + expect((yield* AtomRegistry.getResult(reader.registry, summary)).state).toBe("open"); + expect((yield* AtomRegistry.getResult(reader.registry, list)).entries[0]?.state).toBe("open"); + + for (const [action, expected] of [ + ["close", "closed"], + ["reopen", "open"], + ["merge", "merged"], + ] as const) { + const detailChanged = Latch.makeUnsafe(); + const summaryChanged = Latch.makeUnsafe(); + const listChanged = Latch.makeUnsafe(); + const stops = [ + reader.registry.subscribe(detail, (result) => { + if (AsyncResult.isSuccess(result) && result.value.state === expected) { + detailChanged.openUnsafe(); + } + }), + reader.registry.subscribe(summary, (result) => { + if (AsyncResult.isSuccess(result) && result.value.state === expected) { + summaryChanged.openUnsafe(); + } + }), + reader.registry.subscribe(list, (result) => { + if (AsyncResult.isSuccess(result) && result.value.entries[0]?.state === expected) { + listChanged.openUnsafe(); + } + }), + ]; + yield* Effect.addFinalizer(() => Effect.sync(() => stops.forEach((stop) => stop()))); + const result = yield* Effect.promise(() => + writer.atoms.runAction.run(writer.registry, { + ...target, + input: { ...target.input, action }, + }), + ); + expect(AsyncResult.isSuccess(result)).toBe(true); + // The second client receives only the server push: no local refresh or timer tick. + yield* detailChanged.await; + yield* summaryChanged.await; + yield* listChanged.await; + expect((yield* AtomRegistry.getResult(reader.registry, detail)).state).toBe(expected); + expect((yield* AtomRegistry.getResult(reader.registry, summary)).state).toBe(expected); + expect((yield* AtomRegistry.getResult(reader.registry, list)).entries[0]?.state).toBe( + expected, + ); + stops.forEach((stop) => stop()); + } + }), + ), +); + it.effect("refreshes pull request activity after a comment is updated", () => Effect.scoped( Effect.gen(function* () { From bfd556002c1409f382f970bdf852822d941468b2 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:25:15 -0600 Subject: [PATCH 035/104] fix(devices): identify legacy Android SDK command-line tools (#780) --- .../server/src/device/LocalDeviceHost.test.ts | 55 +++++++++++++++++-- apps/server/src/device/LocalDeviceHost.ts | 14 ++++- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/apps/server/src/device/LocalDeviceHost.test.ts b/apps/server/src/device/LocalDeviceHost.test.ts index 037e05186b..e6b608aa45 100644 --- a/apps/server/src/device/LocalDeviceHost.test.ts +++ b/apps/server/src/device/LocalDeviceHost.test.ts @@ -20,17 +20,21 @@ import * as NetService from "@t3tools/shared/Net"; import * as ServerConfig from "../config.ts"; import * as ProcessRunner from "../processRunner.ts"; -const diagnose = (files: ReadonlyArray, environment: NodeJS.ProcessEnv) => +const diagnose = ( + files: ReadonlyArray, + environment: NodeJS.ProcessEnv, + platform: NodeJS.Platform = "darwin", +) => LocalDeviceHost.__testing.platformReason("android").pipe( Effect.provideService(HostProcessEnvironment, environment), - Effect.provideService(HostProcessPlatform, "darwin"), + Effect.provideService(HostProcessPlatform, platform), Effect.provideService( FileSystem.FileSystem, FileSystem.makeNoop({ exists: (file) => Effect.succeed(files.includes(file)), }), ), - Effect.provide(NodePath.layer), + Effect.provide(platform === "win32" ? NodePath.layerWin32 : NodePath.layerPosix), ); describe("Android SDK availability", () => { @@ -46,7 +50,50 @@ describe("Android SDK availability", () => { const reason = yield* diagnose(["/sdk/platform-tools/adb", "/sdk/emulator/emulator"], { ANDROID_HOME: "/sdk", }); - expect(reason).toContain("Command-line Tools (latest)"); + expect(reason).toContain("Command-line Tools (latest) are missing"); + }), + ); + + it.effect("explains how to upgrade legacy command-line tools", () => + Effect.gen(function* () { + const root = "/test/home/Library/Android/sdk"; + const reason = yield* diagnose( + [`${root}/platform-tools/adb`, `${root}/emulator/emulator`, `${root}/tools/bin/avdmanager`], + { HOME: "/test/home" }, + ); + expect(reason).toContain("older, unsupported version"); + expect(reason).toContain(root); + expect(reason).toContain("Android Studio's SDK Manager under SDK Tools"); + }), + ); + + it.effect("recognizes legacy command-line tools on Windows", () => + Effect.gen(function* () { + const reason = yield* diagnose( + [ + "C:\\Android\\Sdk\\platform-tools\\adb.exe", + "C:\\Android\\Sdk\\emulator\\emulator.exe", + "C:\\Android\\Sdk\\tools\\bin\\avdmanager.bat", + ], + { ANDROID_HOME: "C:\\Android\\Sdk" }, + "win32", + ); + expect(reason).toContain("older, unsupported version"); + }), + ); + + it.effect("accepts latest tools even if legacy tools are installed", () => + Effect.gen(function* () { + const reason = yield* diagnose( + [ + "/sdk/platform-tools/adb", + "/sdk/emulator/emulator", + "/sdk/tools/bin/avdmanager", + "/sdk/cmdline-tools/latest/bin/avdmanager", + ], + { ANDROID_HOME: "/sdk" }, + ); + expect(reason).toBeNull(); }), ); diff --git a/apps/server/src/device/LocalDeviceHost.ts b/apps/server/src/device/LocalDeviceHost.ts index 28143f5b8e..139034a178 100644 --- a/apps/server/src/device/LocalDeviceHost.ts +++ b/apps/server/src/device/LocalDeviceHost.ts @@ -109,8 +109,11 @@ const platformReason = Effect.fn("LocalDeviceHost.platformReason")(function* ( return `Android SDK Platform-Tools are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`; if (!sdk.emulator) return `Android Emulator is missing from ${sdk.root}. Install it in Android Studio's SDK Manager.`; - if (!sdk.avdmanager) + if (!sdk.avdmanager) { + if (sdk.legacyAvdmanager) + return `The Android SDK command-line tools in ${sdk.root} appear to be an older, unsupported version. Install Android SDK Command-line Tools (latest) in Android Studio's SDK Manager under SDK Tools.`; return `Android SDK Command-line Tools (latest) are missing from ${sdk.root}. Install them in Android Studio's SDK Manager.`; + } return null; }); @@ -160,10 +163,15 @@ const androidSdk = Effect.gen(function* () { platform === "win32" ? "avdmanager.bat" : "avdmanager", ), ); - return { root, adb, emulator, avdmanager }; + const legacyAvdmanager = + !avdmanager && + (yield* exists( + path.join(root, "tools", "bin", platform === "win32" ? "avdmanager.bat" : "avdmanager"), + )); + return { root, adb, emulator, avdmanager, legacyAvdmanager }; } } - return { root: null, adb: false, emulator: false, avdmanager: false }; + return { root: null, adb: false, emulator: false, avdmanager: false, legacyAvdmanager: false }; }); const deviceHostEnvironment = ( From 131308440a58f2273bb92173c62cd1db06c22005 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:25:38 -0600 Subject: [PATCH 036/104] feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery --- .../src/environment/ServerEnvironment.ts | 1 + apps/server/src/git/GitManager.ts | 14 ++- .../Layers/ProviderCommandReactor.test.ts | 13 ++- .../Layers/ProviderCommandReactor.ts | 8 +- apps/server/src/vcs/GitVcsDriver.ts | 6 ++ apps/server/src/vcs/GitVcsDriverCore.test.ts | 95 +++++++++++++++++++ apps/server/src/vcs/GitVcsDriverCore.ts | 31 +++++- apps/server/src/ws.ts | 53 ++++++++++- .../web/src/components/BranchToolbar.logic.ts | 14 ++- .../settings/ProjectDefaultsSettings.tsx | 77 ++++++++++++++- .../settings/SettingInheritance.tsx | 10 +- .../settings/scopedSettings.test.ts | 67 +++++++++++++ .../src/components/settings/scopedSettings.ts | 11 ++- .../components/settings/settingsLayout.tsx | 26 ++--- .../src/components/settings/settingsSearch.ts | 7 ++ docs/user/project-settings.md | 4 + packages/contracts/src/environment.test.ts | 10 ++ packages/contracts/src/environment.ts | 6 ++ packages/contracts/src/settings.test.ts | 7 ++ packages/contracts/src/settings.ts | 10 +- packages/contracts/src/t3ProjectFile.test.ts | 7 ++ packages/contracts/src/t3ProjectFile.ts | 8 +- packages/shared/src/projectSettings.test.ts | 12 +++ packages/shared/src/t3ProjectFile.test.ts | 1 + 24 files changed, 464 insertions(+), 34 deletions(-) diff --git a/apps/server/src/environment/ServerEnvironment.ts b/apps/server/src/environment/ServerEnvironment.ts index 951003a271..d63e44bcd9 100644 --- a/apps/server/src/environment/ServerEnvironment.ts +++ b/apps/server/src/environment/ServerEnvironment.ts @@ -226,6 +226,7 @@ export const make = Effect.gen(function* () { threadAutoSettlement: true, threadRestartContinuation: true, projectSettingsOverrides: true, + worktreeSubmodules: true, defaultRuntimeMode: true, threadSnooze: true, environmentThemes: true, diff --git a/apps/server/src/git/GitManager.ts b/apps/server/src/git/GitManager.ts index 42e63304e0..0c93732bfd 100644 --- a/apps/server/src/git/GitManager.ts +++ b/apps/server/src/git/GitManager.ts @@ -2589,11 +2589,15 @@ export const make = Effect.gen(function* () { }); } - const worktree = yield* gitCore.createWorktree({ - cwd: input.cwd, - refName: localPullRequestBranch, - path: null, - }); + const worktree = yield* gitCore.createWorktree( + { cwd: input.cwd, refName: localPullRequestBranch, path: null }, + { + submodules: yield* projectSettingsFor(input).pipe( + Effect.map((settings) => settings.worktreeSubmodules), + Effect.orElseSucceed(() => null), + ), + }, + ); yield* ensureExistingWorktreeUpstream(worktree.worktree.path); yield* maybeRunSetupScript(worktree.worktree.path); diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts index 8ca84fd7c1..19b1a0fc1e 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts @@ -3878,11 +3878,14 @@ describe("ProviderCommandReactor", () => { await waitFor(() => harness.startSession.mock.calls.length === 1); expect(harness.pruneWorktrees).toHaveBeenCalledWith({ cwd: "/tmp/provider-project" }); - expect(harness.createWorktree).toHaveBeenCalledWith({ - cwd: "/tmp/provider-project", - refName: "feature/restore", - path: worktreePath, - }); + expect(harness.createWorktree).toHaveBeenCalledWith( + { + cwd: "/tmp/provider-project", + refName: "feature/restore", + path: worktreePath, + }, + { submodules: null }, + ); expect(harness.createWorktree.mock.invocationCallOrder[0]).toBeLessThan( harness.startSession.mock.invocationCallOrder[0]!, ); diff --git a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts index fd9d22bb73..40caffc583 100644 --- a/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts +++ b/apps/server/src/orchestration/Layers/ProviderCommandReactor.ts @@ -640,8 +640,14 @@ const make = Effect.gen(function* () { }); // A directory deleted without `git worktree remove` leaves an admin entry // that makes `git worktree add` refuse the path; prune clears it. + const submodules = yield* projectSettingsForThread(thread.id).pipe( + Effect.map((settings) => settings.worktreeSubmodules), + Effect.orElseSucceed(() => null), + ); yield* gitWorkflow.pruneWorktrees({ cwd }).pipe( - Effect.andThen(gitWorkflow.createWorktree({ cwd, refName: branch, path: worktreePath })), + Effect.andThen( + gitWorkflow.createWorktree({ cwd, refName: branch, path: worktreePath }, { submodules }), + ), Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) diff --git a/apps/server/src/vcs/GitVcsDriver.ts b/apps/server/src/vcs/GitVcsDriver.ts index 7d89a827ca..5808d8ad4e 100644 --- a/apps/server/src/vcs/GitVcsDriver.ts +++ b/apps/server/src/vcs/GitVcsDriver.ts @@ -30,6 +30,7 @@ import { type VcsRemoveWorktreeInput, type VcsStatusInput, type VcsStatusResult, + type WorktreeSubmodules, } from "@t3tools/contracts"; import { makeGitVcsDriverCore, @@ -128,6 +129,9 @@ export interface CreateWorktreeProgress { total: number; }) => Effect.Effect; readonly onSubmodulesStarted?: () => Effect.Effect; + readonly onSubmodulesDisabled?: (input: { + source: "settings" | "t3.json"; + }) => Effect.Effect; readonly onSubmoduleLine?: (line: string) => Effect.Effect; readonly onSubmodulesFinished?: (input: { ok: boolean; @@ -137,6 +141,8 @@ export interface CreateWorktreeProgress { export interface CreateWorktreeOptions { readonly progress?: CreateWorktreeProgress; + /** Null defers to the newly created checkout's t3.json. */ + readonly submodules?: WorktreeSubmodules | null; } export interface GitCommitProgress { diff --git a/apps/server/src/vcs/GitVcsDriverCore.test.ts b/apps/server/src/vcs/GitVcsDriverCore.test.ts index 4784e4fb12..6f067e049f 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.test.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.test.ts @@ -26,6 +26,7 @@ import { GitCommandError, ReviewDiffPreviewInput, type ReviewDiffFileContentsInput, + type WorktreeSubmodules, } from "@t3tools/contracts"; import { ServerConfig } from "../config.ts"; import { gitCommandDuration } from "../observability/Metrics.ts"; @@ -2394,6 +2395,100 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { }), ); + it.effect("resolves the submodule mode from the option, then t3.json", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const pathService = yield* Path.Path; + + const previousAllowedProtocol = process.env.GIT_ALLOW_PROTOCOL; + process.env.GIT_ALLOW_PROTOCOL = "file"; + yield* Effect.addFinalizer(() => + Effect.sync(() => { + if (previousAllowedProtocol === undefined) { + delete process.env.GIT_ALLOW_PROTOCOL; + } else { + process.env.GIT_ALLOW_PROTOCOL = previousAllowedProtocol; + } + }), + ); + + // inner -> nested, so a recursive init populates nested/NESTED.md and + // a top-level init leaves it empty. + const nestedRepo = yield* makeTmpDir("git-nested-"); + yield* initRepoWithCommit(nestedRepo); + yield* writeTextFile(nestedRepo, "NESTED.md", "# nested\n"); + yield* git(nestedRepo, ["add", "."]); + yield* git(nestedRepo, ["commit", "-m", "nested"]); + const innerRepo = yield* makeTmpDir("git-inner-"); + yield* initRepoWithCommit(innerRepo); + yield* writeTextFile(innerRepo, "INNER.md", "# inner\n"); + yield* git(innerRepo, ["submodule", "add", nestedRepo, "nested"]); + yield* git(innerRepo, ["add", "."]); + yield* git(innerRepo, ["commit", "-m", "inner"]); + + const cwd = yield* makeTmpDir(); + const { initialBranch } = yield* initRepoWithCommit(cwd); + yield* git(cwd, ["submodule", "add", innerRepo, "inner"]); + yield* git(cwd, ["commit", "-m", "add submodule"]); + const driver = yield* GitVcsDriver.GitVcsDriver; + const worktreesDir = yield* makeTmpDir("git-worktrees-"); + + const createWithMode = Effect.fn(function* ( + fileMode: WorktreeSubmodules, + branch: string, + submodules: WorktreeSubmodules | null = null, + ) { + yield* writeTextFile(cwd, "t3.json", `{ "worktreeSubmodules": "${fileMode}" }`); + yield* git(cwd, ["add", "t3.json"]); + // Consecutive cases may reuse a file mode to test the option alone. + yield* git(cwd, ["commit", "--allow-empty", "-m", `submodules: ${fileMode}`]); + const worktreePath = pathService.join(worktreesDir, branch); + const disabled = yield* Ref.make<"settings" | "t3.json" | false>(false); + yield* driver.createWorktree( + { cwd, path: worktreePath, refName: initialBranch, newRefName: branch }, + { + submodules, + progress: { onSubmodulesDisabled: ({ source }) => Ref.set(disabled, source) }, + }, + ); + return { + disabled: yield* Ref.get(disabled), + inner: yield* fileSystem.exists(pathService.join(worktreePath, "inner", "INNER.md")), + nested: yield* fileSystem.exists( + pathService.join(worktreePath, "inner", "nested", "NESTED.md"), + ), + }; + }); + + assert.deepEqual(yield* createWithMode("recursive", "recursive"), { + disabled: false, + inner: true, + nested: true, + }); + assert.deepEqual(yield* createWithMode("top-level", "top-level"), { + disabled: false, + inner: true, + nested: false, + }); + // A resolved setting outranks the file in both directions. + assert.deepEqual(yield* createWithMode("recursive", "setting-none", "none"), { + disabled: "settings", + inner: false, + nested: false, + }); + assert.deepEqual(yield* createWithMode("none", "setting-wins", "top-level"), { + disabled: false, + inner: true, + nested: false, + }); + assert.deepEqual(yield* createWithMode("none", "none"), { + disabled: "t3.json", + inner: false, + nested: false, + }); + }), + ); + it.effect("allows worktree removal to run longer than the default command timeout", () => Effect.gen(function* () { const delegate = yield* ChildProcessSpawner.ChildProcessSpawner; diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index bec2aa39eb..ebae13509a 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -20,6 +20,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { GitCommandError, + T3_PROJECT_FILE_NAME, type ReviewDiffFileContentsInput, type ReviewDiffPreviewInput, type ReviewDiffFileStat, @@ -30,6 +31,7 @@ import { dedupeRemoteBranchesWithLocalMatches, normalizeGitRemoteUrl } from "@t3 import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { compactTraceAttributes } from "@t3tools/shared/observability"; import { decodeJsonResult } from "@t3tools/shared/schemaJson"; +import { parseT3ProjectFile } from "@t3tools/shared/t3ProjectFile"; import { gitCommandDuration, gitCommandsTotal, withMetrics } from "../observability/Metrics.ts"; import * as GitVcsDriver from "./GitVcsDriver.ts"; import { @@ -3102,7 +3104,30 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* const hasSubmodules = yield* fileSystem .exists(path.join(worktreePath, ".gitmodules")) .pipe(Effect.orElseSucceed(() => false)); - if (hasSubmodules) { + const submoduleSetting = options?.submodules; + const submoduleMode = !hasSubmodules + ? "none" + : submoduleSetting != null + ? submoduleSetting + : yield* fileSystem.readFileString(path.join(worktreePath, T3_PROJECT_FILE_NAME)).pipe( + Effect.flatMap((contents) => { + const file = parseT3ProjectFile(contents); + return file === null + ? Effect.logWarning("t3.json is invalid; initializing submodules recursively", { + worktreePath, + }).pipe(Effect.as("recursive" as const)) + : Effect.succeed(file.worktreeSubmodules ?? "recursive"); + }), + Effect.orElseSucceed(() => "recursive" as const), + ); + if (hasSubmodules && submoduleMode === "none") { + if (progress?.onSubmodulesDisabled) { + yield* progress.onSubmodulesDisabled({ + source: submoduleSetting == null ? "t3.json" : "settings", + }); + } + } + if (submoduleMode !== "none") { if (progress?.onSubmodulesStarted) { yield* progress.onSubmodulesStarted(); } @@ -3110,7 +3135,9 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* yield* runGit( "GitVcsDriver.createWorktree.updateSubmodules", worktreePath, - ["submodule", "update", "--init", "--recursive"], + submoduleMode === "recursive" + ? ["submodule", "update", "--init", "--recursive"] + : ["submodule", "update", "--init"], onSubmoduleLine ? { env: { LC_ALL: "C" }, diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index b4b4035f5a..2f34051340 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -91,6 +91,7 @@ import { type WorktreeSetupSnapshot, } from "@t3tools/contracts"; import { resolveServerBackgroundActivitySettings } from "@t3tools/shared/backgroundActivitySettings"; +import { resolveProjectSettings } from "@t3tools/shared/projectSettings"; import { isDriveOrPosixAbsolutePath, isWindowsAbsolutePath } from "@t3tools/shared/path"; import { HttpRouter, HttpServerRequest, HttpServerRespondable } from "effect/unstable/http"; import { RpcSerialization, RpcServer } from "effect/unstable/rpc"; @@ -1072,6 +1073,41 @@ const makeWsRpcLayer = ( return output; }); + // The branch checked out into a new worktree may have its own t3.json, + // so a null settings value intentionally leaves that read to the driver. + const resolveBootstrapWorktreeSubmodules = Effect.fnUntraced(function* (input: { + readonly threadId: ThreadId; + readonly projectId: ProjectId | null; + }) { + const settings = yield* serverSettings.getSettings.pipe(Effect.orElseSucceed(() => null)); + if (settings === null) return null; + const projectId = + input.projectId ?? + (yield* projectionSnapshotQuery.getThreadShellById(input.threadId).pipe( + Effect.map((thread) => Option.getOrNull(thread)?.projectId ?? null), + Effect.orElseSucceed(() => null), + )); + const project = + projectId === null + ? null + : yield* projectionSnapshotQuery.getProjectShellById(projectId).pipe( + Effect.map(Option.getOrNull), + Effect.orElseSucceed(() => null), + ); + return resolveProjectSettings(settings, projectId, project).settings.worktreeSubmodules; + }); + + const resolveWorktreeSubmodulesForCwd = Effect.fnUntraced(function* (cwd: string) { + const settings = yield* serverSettings.getSettings.pipe(Effect.orElseSucceed(() => null)); + if (settings === null) return null; + const project = yield* projectionSnapshotQuery.getActiveProjectByWorkspaceRoot(cwd).pipe( + Effect.map(Option.getOrNull), + Effect.orElseSucceed(() => null), + ); + return resolveProjectSettings(settings, project?.id ?? null, project).settings + .worktreeSubmodules; + }); + const dispatchBootstrapTurnStart = ( command: Extract, ): Effect.Effect<{ readonly sequence: number }, OrchestrationDispatchCommandError> => @@ -1495,6 +1531,10 @@ const makeWsRpcLayer = ( } yield* worktreeSetupTracker.stageStatus(threadId, "checkout", "running"); let checkoutTotal: number | null = null; + const submodules = yield* resolveBootstrapWorktreeSubmodules({ + threadId, + projectId: targetProjectId ?? null, + }); const worktree = yield* gitWorkflow.createWorktree( { cwd: prepareWorktree.projectCwd, @@ -1504,6 +1544,7 @@ const makeWsRpcLayer = ( path: null, }, { + submodules, progress: { // Git has registered the directory at this point, so a // cancel during the submodule step can still remove it. @@ -1533,6 +1574,13 @@ const makeWsRpcLayer = ( worktreeSetupTracker.stageStatus(threadId, "submodules", "running"), ), ), + onSubmodulesDisabled: ({ source }) => + worktreeSetupTracker.stageStatus( + threadId, + "submodules", + "skipped", + `disabled in ${source}`, + ), onSubmoduleLine: (line) => { const submodulePath = /Submodule path '([^']+)'/.exec(line)?.[1]; return submodulePath === undefined @@ -3609,7 +3657,10 @@ const makeWsRpcLayer = ( [WS_METHODS.vcsCreateWorktree]: (input) => observeRpcEffect( WS_METHODS.vcsCreateWorktree, - gitWorkflow.createWorktree(input).pipe(Effect.tap(() => refreshGitStatus(input.cwd))), + resolveWorktreeSubmodulesForCwd(input.cwd).pipe( + Effect.flatMap((submodules) => gitWorkflow.createWorktree(input, { submodules })), + Effect.tap(() => refreshGitStatus(input.cwd)), + ), { "rpc.aggregate": "vcs" }, ), [WS_METHODS.vcsRemoveWorktree]: (input) => diff --git a/apps/web/src/components/BranchToolbar.logic.ts b/apps/web/src/components/BranchToolbar.logic.ts index bd892b5a4d..5a3367c8ce 100644 --- a/apps/web/src/components/BranchToolbar.logic.ts +++ b/apps/web/src/components/BranchToolbar.logic.ts @@ -1,4 +1,10 @@ -import type { EnvironmentId, EnvironmentMachineKind, VcsRef, ProjectId } from "@t3tools/contracts"; +import type { + EnvironmentId, + EnvironmentMachineKind, + VcsRef, + ProjectId, + WorktreeSubmodules, +} from "@t3tools/contracts"; import * as Schema from "effect/Schema"; import { toSortableTimestamp } from "../lib/threadSort"; export { @@ -17,6 +23,12 @@ export interface EnvironmentOption { export const EnvMode = Schema.Literals(["local", "worktree"]); export type EnvMode = typeof EnvMode.Type; +export const WORKTREE_SUBMODULES_LABELS: Record = { + recursive: "Recursive", + "top-level": "Top level only", + none: "Do not initialize", +}; + const GENERIC_LOCAL_ENVIRONMENT_LABELS = new Set(["local", "local environment"]); function normalizeDisplayLabel(value: string | null | undefined): string | null { diff --git a/apps/web/src/components/settings/ProjectDefaultsSettings.tsx b/apps/web/src/components/settings/ProjectDefaultsSettings.tsx index 57991dd4bf..8c34ca7fa1 100644 --- a/apps/web/src/components/settings/ProjectDefaultsSettings.tsx +++ b/apps/web/src/components/settings/ProjectDefaultsSettings.tsx @@ -3,6 +3,7 @@ import { EnvironmentId, type ModelSelection, type ProviderInstanceId, + type WorktreeSubmodules, } from "@t3tools/contracts"; import { createModelSelection } from "@t3tools/shared/model"; import { useNavigate } from "@tanstack/react-router"; @@ -17,7 +18,7 @@ import { } from "../../providerInstances"; import { useEnvironments } from "../../state/environments"; import { EMPTY_SERVER_PROVIDERS } from "../../state/server"; -import { resolveEnvModeLabel } from "../BranchToolbar.logic"; +import { resolveEnvModeLabel, WORKTREE_SUBMODULES_LABELS } from "../BranchToolbar.logic"; import { ProviderModelPicker } from "../chat/ProviderModelPicker"; import { runtimeModeConfig, runtimeModeOptions } from "../chat/runtimeModeConfig"; import { PULL_REQUEST_MERGE_METHOD_LABELS } from "../pullRequest/pullRequestDetail.logic"; @@ -46,6 +47,11 @@ import { * environment defaults at an environment scope and project overrides at a * project or checkout scope; the scoped hooks route the write. */ +const WORKTREE_SUBMODULES_OPTIONS = ["recursive", "top-level", "none"] as const; +function isWorktreeSubmodules(value: string | null): value is WorktreeSubmodules { + return value !== null && (WORKTREE_SUBMODULES_OPTIONS as readonly string[]).includes(value); +} + export function ProjectDefaultsSettings({ category }: { category: ProjectSettingsCategory }) { const { scope, target, targets, connectedEnvironments } = useSettingsScope(); const settings = useScopedSettings(); @@ -71,6 +77,13 @@ export function ProjectDefaultsSettings({ category }: { category: ProjectSetting const mixedPermissions = useScopedSettingsMixed(["defaultRuntimeMode"]); const PermissionIcon = runtimeModeConfig[settings.defaultRuntimeMode].icon; const mixedWorkspace = useScopedSettingsMixed(["defaultThreadEnvMode"]); + const mixedSubmodules = useScopedSettingsMixed(["worktreeSubmodules"]); + const submoduleSettingSupported = + connectedEnvironments.length > 0 && + connectedEnvironments.every( + (environment) => + environment.serverConfig?.environment.capabilities.worktreeSubmodules === true, + ); const mixedBrowser = useScopedSettingsMixed(["enableAgentBrowserAccess"]); const mixedDevice = useScopedSettingsMixed(["enableAgentDeviceAccess"]); const mixedAutoPull = useScopedSettingsMixed(["defaultAutoPull"]); @@ -89,6 +102,7 @@ export function ProjectDefaultsSettings({ category }: { category: ProjectSetting category === "general" && checkout ? checkout.workspaceRoot : null, ); const repositoryEnvMode = t3File.file?.defaultThreadEnvMode ?? null; + const repositorySubmodules = t3File.file?.worktreeSubmodules ?? null; const inheritedEnvModeLabel = workspaceSource === "project" ? null @@ -285,6 +299,67 @@ export function ProjectDefaultsSettings({ category }: { category: ProjectSetting } /> + updateSettings({ worktreeSubmodules: null })} + /> + ) : null + } + control={ + + } + /> { ]); }); + it.each([undefined, false])( + "does not acknowledge worktree submodule writes when capability is %s", + async (capability) => { + const older = environment("Server", { + capabilities: capability === undefined ? {} : { worktreeSubmodules: capability }, + }); + for (const scope of [ + resolveSettingsScope({ machine: older.environmentId }, [], [older]), + checkout, + ]) { + const plan = planScopedSettingsPatch(scope, [older], { worktreeSubmodules: "none" }); + expect(plan.serverWrites).toEqual([]); + const persistServer = vi.fn().mockResolvedValue({ _tag: "Success" }); + const result = await persistScopedSettingsPatch(plan, persistServer, vi.fn()); + expect(result.savedEnvironmentCount).toBe(0); + expect(persistServer).not.toHaveBeenCalled(); + } + }, + ); + + it("writes worktree submodule settings only to an advertising environment", () => { + const capable = environment("Capable", { capabilities: { worktreeSubmodules: true } }); + const older = environment("Older", { capabilities: { worktreeSubmodules: false } }); + const scope = resolveSettingsScope({}, [], [capable, older]); + const plan = planScopedSettingsPatch(scope, [capable, older], { + worktreeSubmodules: "top-level", + }); + expect(plan.serverWrites.map((write) => write.environmentId)).toEqual([capable.environmentId]); + expect(plan.skippedEnvironments).toEqual([ + { environmentId: older.environmentId, label: older.label }, + ]); + }); + it("reports disconnected targets while preserving local-only preferences", async () => { const persistServer = vi.fn().mockResolvedValue({ _tag: "Success" }); const persistClient = vi.fn(); @@ -423,6 +457,39 @@ describe("scoped settings writes", () => { ).toEqual({ projectSettingsOverrides: { [projectId]: null } }); }); + it("does not acknowledge submodule override resets on an older selected server", async () => { + const capable = environment("Laptop", { + capabilities: { worktreeSubmodules: true }, + settings: { + projectSettingsOverrides: { [laptopProjectId]: { worktreeSubmodules: "none" } }, + }, + }); + const older = environment("Server", { + capabilities: { worktreeSubmodules: false }, + settings: { projectSettingsOverrides: { [projectId]: { worktreeSubmodules: "none" } } }, + }); + const cleared = planScopedSettingsClear(project, [capable, older], ["worktreeSubmodules"]); + expect(cleared.serverWrites.map((write) => write.environmentId)).toEqual([ + capable.environmentId, + ]); + expect(cleared.skippedEnvironments).toEqual([ + { environmentId: older.environmentId, label: older.label }, + ]); + const named = planProjectOverridesClear( + [capable, older], + [ + { environmentId: capable.environmentId, projectId: laptopProjectId }, + { environmentId: older.environmentId, projectId }, + ], + ["worktreeSubmodules"], + ); + expect(named.serverWrites.map((write) => write.environmentId)).toEqual([capable.environmentId]); + const persistServer = vi.fn().mockResolvedValue({ _tag: "Success" }); + const result = await persistScopedSettingsPatch(cleared, persistServer, vi.fn()); + expect(result.savedEnvironmentCount).toBe(1); + expect(result.failedEnvironments).toEqual(cleared.skippedEnvironments); + }); + it("never substitutes an environment-default write for an invalid scope", () => { const scope = resolveSettingsScope({ machine: "removed" }, [group], environments); const plan = planScopedSettingsPatch(scope, environments, { enableAgentBrowserAccess: false }); diff --git a/apps/web/src/components/settings/scopedSettings.ts b/apps/web/src/components/settings/scopedSettings.ts index b103174575..ba6ccb35e4 100644 --- a/apps/web/src/components/settings/scopedSettings.ts +++ b/apps/web/src/components/settings/scopedSettings.ts @@ -31,6 +31,7 @@ interface ScopedSettingsEnvironment { readonly environment?: { readonly capabilities: { readonly projectSettingsOverrides?: boolean | undefined; + readonly worktreeSubmodules?: boolean | undefined; readonly projectDefaults?: boolean | undefined; readonly threadRestartContinuation?: boolean | undefined; readonly threadAutoSettlement?: boolean | undefined; @@ -221,6 +222,7 @@ function supportsScopedPatch( const capabilities = environment.serverConfig?.environment?.capabilities; if (projectScope && capabilities?.projectSettingsOverrides !== true) return false; return keys.every((key) => { + if (key === "worktreeSubmodules") return capabilities?.worktreeSubmodules === true; if (key === "defaultRuntimeMode") return capabilities?.defaultRuntimeMode === true; if (key === "projectSettingsOverrides" || key === "pullRequestMergeMethod") { return capabilities?.projectSettingsOverrides === true; @@ -344,8 +346,11 @@ export function planScopedSettingsClear( ) { const serverWrites = scope.kind === "project" || scope.kind === "checkout" - ? projectOverrideWrites(scope, environments, (_current, settings, projectId) => - clearProjectSettingsOverrides(settings, projectId, keys), + ? projectOverrideWrites( + scope, + environments.filter((environment) => supportsScopedPatch(environment, keys, true)), + (_current, settings, projectId) => + clearProjectSettingsOverrides(settings, projectId, keys), ) : []; return { @@ -404,7 +409,7 @@ export function planProjectOverridesClear( if ( !environment?.serverConfig || environment.connection.phase !== "connected" || - environment.serverConfig.environment?.capabilities.projectSettingsOverrides !== true + !supportsScopedPatch(environment, keys, true) ) continue; const settings = environment.serverConfig.settings; diff --git a/apps/web/src/components/settings/settingsLayout.tsx b/apps/web/src/components/settings/settingsLayout.tsx index 57f23dd5b9..34c9c7fff8 100644 --- a/apps/web/src/components/settings/settingsLayout.tsx +++ b/apps/web/src/components/settings/settingsLayout.tsx @@ -271,6 +271,7 @@ export function SettingsRow({ status, resetAction, onResetOverride, + disableOverrideReset = false, control, serverScoped = false, settingKeys = EMPTY_SETTING_KEYS, @@ -285,6 +286,8 @@ export function SettingsRow({ resetAction?: ReactNode; /** Replaces the default override clear for rows with side effects beyond the settings key. */ onResetOverride?: () => void; + /** Hide project reset when a selected server cannot apply this setting. */ + disableOverrideReset?: boolean; control?: ReactNode; serverScoped?: boolean; settingKeys?: readonly (keyof ServerSettings)[]; @@ -350,17 +353,18 @@ export function SettingsRow({ ]; }); }, [context, isProjectScope, scopedKeys]); - const renderedReset = unavailable ? null : isProjectScope && scopedKeys.length > 0 ? ( - source === "project" || source === "mixed" ? ( - (onResetOverride ? onResetOverride() : clearOverrides(scopedKeys))} - /> - ) : null - ) : ( - resetAction - ); + const renderedReset = + unavailable || disableOverrideReset ? null : isProjectScope && scopedKeys.length > 0 ? ( + source === "project" || source === "mixed" ? ( + (onResetOverride ? onResetOverride() : clearOverrides(scopedKeys))} + /> + ) : null + ) : ( + resetAction + ); const inertControl = (message: string) => ( { expect(decodeDescriptor(descriptor).capabilities.pullRequests).toBeUndefined(); }); + it("requires an explicit worktree submodules capability", () => { + expect(decodeDescriptor(descriptor).capabilities.worktreeSubmodules).toBeUndefined(); + expect( + decodeDescriptor({ + ...descriptor, + capabilities: { ...descriptor.capabilities, worktreeSubmodules: true }, + }).capabilities.worktreeSubmodules, + ).toBe(true); + }); + it("preserves an advertised pull-request capability", () => { expect( decodeDescriptor({ diff --git a/packages/contracts/src/environment.ts b/packages/contracts/src/environment.ts index 5760c38758..49372cdbb4 100644 --- a/packages/contracts/src/environment.ts +++ b/packages/contracts/src/environment.ts @@ -58,6 +58,10 @@ export const ExecutionEnvironmentPlatform = Schema.Struct({ */ export const ThreadEnvMode = Schema.Literals(["local", "worktree"]); export type ThreadEnvMode = typeof ThreadEnvMode.Type; + +/** How new worktrees initialize git submodules. */ +export const WorktreeSubmodules = Schema.Literals(["recursive", "top-level", "none"]); +export type WorktreeSubmodules = typeof WorktreeSubmodules.Type; export type ExecutionEnvironmentPlatform = typeof ExecutionEnvironmentPlatform.Type; /** How a server can replace itself with another version when asked over RPC. @@ -109,6 +113,8 @@ export const ExecutionEnvironmentCapabilities = Schema.Struct({ threadRestartContinuation: Schema.optionalKey(Schema.Boolean), /** Server resolves `projectSettingsOverrides`; older servers ignore the key. */ projectSettingsOverrides: Schema.optionalKey(Schema.Boolean), + /** Server applies the worktree submodule initialization setting. */ + worktreeSubmodules: Schema.optionalKey(Schema.Boolean), /** Server accepts and applies the default permission mode for new threads. */ defaultRuntimeMode: Schema.optionalKey(Schema.Boolean), /** Server understands thread.snooze / thread.unsnooze commands. Same diff --git a/packages/contracts/src/settings.test.ts b/packages/contracts/src/settings.test.ts index bc124651cb..471fde67d7 100644 --- a/packages/contracts/src/settings.test.ts +++ b/packages/contracts/src/settings.test.ts @@ -812,6 +812,13 @@ describe("provider enabled defaults", () => { }); describe("ServerSettings worktree defaults", () => { + it("defaults submodules to repository settings and accepts explicit modes", () => { + expect(decodeServerSettings({}).worktreeSubmodules).toBeNull(); + for (const mode of ["recursive", "top-level", "none"] as const) { + expect(decodeServerSettingsPatch({ worktreeSubmodules: mode }).worktreeSubmodules).toBe(mode); + } + expect(decodeServerSettingsPatch({ worktreeSubmodules: null }).worktreeSubmodules).toBeNull(); + }); it("defaults start-from-origin on for legacy configs", () => { expect(decodeServerSettings({}).newWorktreesStartFromOrigin).toBe(true); }); diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index a9569dedbd..0b59ae9c50 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -7,13 +7,14 @@ import * as Schema from "effect/Schema"; import * as SchemaTransformation from "effect/SchemaTransformation"; import { ForwardCompatibleNullable, + ForwardCompatibleOptional, NonNegativeInt, ProjectId, TrimmedNonEmptyString, TrimmedString, } from "./baseSchemas.ts"; import { UsageLimitSourceId } from "./usageLimitSourceId.ts"; -import { EnvironmentMachineKind, ThreadEnvMode } from "./environment.ts"; +import { EnvironmentMachineKind, ThreadEnvMode, WorktreeSubmodules } from "./environment.ts"; import { KeybindingShortcut } from "./keybindings.ts"; import { CustomModelSetting, @@ -1110,6 +1111,7 @@ export const PROJECT_SCOPED_SERVER_SETTING_KEYS = [ "defaultRuntimeMode", "defaultThreadEnvMode", "newWorktreesStartFromOrigin", + "worktreeSubmodules", "defaultAutoPull", "defaultProjectScripts", "enableAgentBrowserAccess", @@ -1135,6 +1137,7 @@ export const ProjectSettingsOverrides = Schema.Struct({ defaultRuntimeMode: Schema.optionalKey(RuntimeMode), defaultThreadEnvMode: Schema.optionalKey(ThreadEnvMode), newWorktreesStartFromOrigin: Schema.optionalKey(Schema.Boolean), + worktreeSubmodules: ForwardCompatibleOptional(WorktreeSubmodules), defaultAutoPull: Schema.optionalKey(Schema.Boolean), defaultProjectScripts: Schema.optionalKey(Schema.Array(ProjectScript)), enableAgentBrowserAccess: Schema.optionalKey(Schema.Boolean), @@ -1284,6 +1287,10 @@ export const ServerSettings = Schema.Struct({ newWorktreesStartFromOrigin: Schema.Boolean.pipe( Schema.withDecodingDefault(Effect.succeed(true)), ), + /** Null defers to the newly created checkout's t3.json, then recursive. */ + worktreeSubmodules: ForwardCompatibleNullable(WorktreeSubmodules).pipe( + Schema.withDecodingDefault(Effect.succeed(null)), + ), addProjectBaseDirectory: TrimmedString.pipe(Schema.withDecodingDefault(Effect.succeed(""))), textGenerationModelSelection: ModelSelection.pipe( Schema.withDecodingDefault( @@ -1572,6 +1579,7 @@ export const ServerSettingsPatch = Schema.Struct({ environmentIcon: Schema.optionalKey(Schema.NullOr(EnvironmentMachineKind)), defaultThreadEnvMode: Schema.optionalKey(ThreadEnvMode), newWorktreesStartFromOrigin: Schema.optionalKey(Schema.Boolean), + worktreeSubmodules: Schema.optionalKey(Schema.NullOr(WorktreeSubmodules)), addProjectBaseDirectory: Schema.optionalKey(TrimmedString), textGenerationModelSelection: Schema.optionalKey(ModelSelectionPatch), sourceControlWritingStyle: Schema.optionalKey( diff --git a/packages/contracts/src/t3ProjectFile.test.ts b/packages/contracts/src/t3ProjectFile.test.ts index 4352449c7a..b475b96347 100644 --- a/packages/contracts/src/t3ProjectFile.test.ts +++ b/packages/contracts/src/t3ProjectFile.test.ts @@ -60,4 +60,11 @@ describe("T3ProjectFile", () => { expect(decode({ defaultThreadEnvMode: "local" }).defaultThreadEnvMode).toBe("local"); expect(() => decode({ defaultThreadEnvMode: "remote" })).toThrow(); }); + + it("decodes worktree submodule modes and rejects unknown modes", () => { + for (const mode of ["recursive", "top-level", "none"] as const) { + expect(decode({ worktreeSubmodules: mode }).worktreeSubmodules).toBe(mode); + } + expect(() => decode({ worktreeSubmodules: "shallow" })).toThrow(); + }); }); diff --git a/packages/contracts/src/t3ProjectFile.ts b/packages/contracts/src/t3ProjectFile.ts index 08f7b17e27..bbcee1a540 100644 --- a/packages/contracts/src/t3ProjectFile.ts +++ b/packages/contracts/src/t3ProjectFile.ts @@ -1,7 +1,7 @@ import * as Schema from "effect/Schema"; import * as SchemaTransformation from "effect/SchemaTransformation"; -import { ThreadEnvMode } from "./environment.ts"; +import { ThreadEnvMode, WorktreeSubmodules } from "./environment.ts"; import { ProjectScriptIcon } from "./orchestration.ts"; /** File name of the checked-in T3 project file, resolved at the workspace root. */ @@ -86,6 +86,12 @@ export const T3ProjectFile = Schema.Struct({ 'Where new threads start for this repository: "worktree" for a fresh git worktree, "local" for the current checkout. A per-project setting in Pylon overrides this; when neither is set, the global default applies.', }), ), + worktreeSubmodules: Schema.optionalKey( + WorktreeSubmodules.annotate({ + description: + 'How new worktrees populate git submodules: "recursive" (default), "top-level", or "none".', + }), + ), scripts: Schema.optionalKey( Schema.Array(T3ProjectFileScript) .annotate({ diff --git a/packages/shared/src/projectSettings.test.ts b/packages/shared/src/projectSettings.test.ts index 26efbda8dd..cd7ed360dd 100644 --- a/packages/shared/src/projectSettings.test.ts +++ b/packages/shared/src/projectSettings.test.ts @@ -19,6 +19,18 @@ const projectId = ProjectId.make("project-a"); const otherProjectId = ProjectId.make("project-b"); describe("resolveProjectSettings", () => { + it("lets a project override the worktree submodule mode and restore inheritance", () => { + const settings = applyServerSettingsPatch(DEFAULT_SERVER_SETTINGS, { + worktreeSubmodules: "top-level", + projectSettingsOverrides: { [projectId]: { worktreeSubmodules: "none" } }, + }); + expect(resolveProjectSettings(settings, projectId).settings.worktreeSubmodules).toBe("none"); + expect(resolveProjectSettings(settings, otherProjectId).settings.worktreeSubmodules).toBe( + "top-level", + ); + const inherited = clearProjectSettingsOverrides(settings, projectId, ["worktreeSubmodules"]); + expect(inherited).toBeNull(); + }); it("inherits every scopable key when the project has no overrides", () => { const resolved = resolveProjectSettings(DEFAULT_SERVER_SETTINGS, projectId); expect(resolved.settings).toBe(DEFAULT_SERVER_SETTINGS); diff --git a/packages/shared/src/t3ProjectFile.test.ts b/packages/shared/src/t3ProjectFile.test.ts index 924a01c441..42045cc428 100644 --- a/packages/shared/src/t3ProjectFile.test.ts +++ b/packages/shared/src/t3ProjectFile.test.ts @@ -36,6 +36,7 @@ describe("buildT3ProjectFileJsonSchema", () => { "defaultThreadEnvMode", "iconPath", "scripts", + "worktreeSubmodules", ]); expect(schema.required).toBeUndefined(); expect(schema.properties.iconPath?.description).toContain("Workspace-relative path"); From 11260d3496e90fedf535352c2380951ada89f5cc Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:26:08 -0600 Subject: [PATCH 037/104] perf(server): scope worktree fetch and parallel checkout (#820) --- apps/server/src/git/GitWorkflowService.ts | 1 + apps/server/src/server.test.ts | 1 + apps/server/src/vcs/GitVcsDriverCore.test.ts | 168 ++++++++++++++++++- apps/server/src/vcs/GitVcsDriverCore.ts | 146 +++++++++++----- apps/server/src/ws.ts | 1 + 5 files changed, 275 insertions(+), 42 deletions(-) diff --git a/apps/server/src/git/GitWorkflowService.ts b/apps/server/src/git/GitWorkflowService.ts index 9f3176cee8..d11109c2fd 100644 --- a/apps/server/src/git/GitWorkflowService.ts +++ b/apps/server/src/git/GitWorkflowService.ts @@ -86,6 +86,7 @@ export class GitWorkflowService extends Context.Service< readonly fetchRemote: (input: { readonly cwd: string; readonly remoteName: string; + readonly refName?: string; }) => Effect.Effect; readonly remoteExists: (input: { readonly cwd: string; diff --git a/apps/server/src/server.test.ts b/apps/server/src/server.test.ts index 04c71a54ac..ad3522a770 100644 --- a/apps/server/src/server.test.ts +++ b/apps/server/src/server.test.ts @@ -11224,6 +11224,7 @@ it.layer(NodeServices.layer)("server router seam", (it) => { assert.deepEqual(fetchRemote.mock.calls[0]?.[0], { cwd: "/tmp/project", remoteName: "origin", + refName: "main", }); assert.deepEqual(remoteBranchExists.mock.calls[0]?.[0], { cwd: "/tmp/project", diff --git a/apps/server/src/vcs/GitVcsDriverCore.test.ts b/apps/server/src/vcs/GitVcsDriverCore.test.ts index 6f067e049f..691909d25d 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.test.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.test.ts @@ -2191,6 +2191,57 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { }); describe("worktree operations", () => { + it.effect( + "uses parallel checkout while preserving filters, hooks, and configured workers", + () => + Effect.gen(function* () { + const cwd = yield* makeTmpDir(); + const { initialBranch } = yield* initRepoWithCommit(cwd); + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const driver = yield* GitVcsDriver.GitVcsDriver; + yield* git(cwd, ["config", "filter.test.smudge", "sed s/original/filtered/g"]); + yield* writeTextFile(cwd, ".gitattributes", "asset.txt filter=test\n"); + yield* writeTextFile(cwd, "asset.txt", "original\n"); + yield* git(cwd, ["add", "."]); + yield* git(cwd, ["commit", "-m", "filtered asset"]); + yield* writeTextFile( + cwd, + ".git/hooks/post-checkout", + "#!/bin/sh\ngit config checkout.workers > checkout-workers\nexit 0\n", + ); + yield* fs.chmod(path.join(cwd, ".git/hooks/post-checkout"), 0o755); + + for (const [configured, expected] of [ + [null, "0"], + ["1", "1"], + ] as const) { + if (configured !== null) yield* git(cwd, ["config", "checkout.workers", configured]); + const worktreePath = path.join( + yield* makeTmpDir("git-worktrees-"), + `workers-${expected}`, + ); + yield* driver.createWorktree({ + cwd, + path: worktreePath, + refName: initialBranch, + newRefName: `feature/workers-${expected}`, + }); + assert.equal( + yield* fs.readFileString(path.join(worktreePath, "checkout-workers")), + `${expected}\n`, + ); + assert.equal( + yield* fs.readFileString(path.join(worktreePath, "asset.txt")), + "filtered\n", + ); + assert.equal( + yield* git(worktreePath, ["rev-parse", "HEAD"]), + yield* git(cwd, ["rev-parse", "HEAD"]), + ); + } + }), + ); it("parses checkout progress lines from git's stderr", () => { assert.deepStrictEqual(parseGitCheckoutProgressLine("Updating files: 78% (2104/2700)"), { percent: 78, @@ -2320,11 +2371,11 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { }), ); - it.effect("reports checkout progress while creating a worktree", () => + it.effect("reports checkout progress during parallel worktree creation", () => Effect.gen(function* () { const cwd = yield* makeTmpDir(); const { initialBranch } = yield* initRepoWithCommit(cwd); - for (let index = 0; index < 5; index += 1) { + for (let index = 0; index < 200; index += 1) { yield* writeTextFile(cwd, `file-${index}.txt`, `${index}\n`); } yield* git(cwd, ["add", "."]); @@ -2358,7 +2409,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { const updates = yield* Ref.get(seen); assert.isAbove(updates.length, 1); assert.equal(updates.at(-1)?.percent, 100); - assert.equal(updates.at(-1)?.total, 6); + assert.equal(updates.at(-1)?.total, 201); const completed = updates.map((update) => update.completed); assert.deepEqual( completed, @@ -2688,6 +2739,67 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { }); describe("remote operations", () => { + for (const failure of ["offline", "auth", "timeout"] as const) { + it.effect(`does not retry a scoped fetch after ${failure}`, () => + Effect.gen(function* () { + const cwd = yield* makeTmpDir(); + yield* initRepoWithCommit(cwd); + yield* git(cwd, ["remote", "add", "origin", "https://example.invalid/repo.git"]); + const delegate = yield* ChildProcessSpawner.ChildProcessSpawner; + const started = yield* Deferred.make(); + const attempts: Array> = []; + const spawner = ChildProcessSpawner.make((command) => + Effect.gen(function* () { + if (!ChildProcess.isStandardCommand(command)) + return yield* Effect.die("unexpected command"); + if (command.args[0] !== "fetch") return yield* delegate.spawn(command); + attempts.push(command.args); + yield* Deferred.succeed(started, undefined); + return ChildProcessSpawner.makeHandle({ + ...makeNonRepositoryHandle(), + exitCode: + failure === "timeout" + ? Effect.never + : Effect.succeed(ChildProcessSpawner.ExitCode(128)), + stderr: Stream.encodeText( + Stream.make( + failure === "auth" + ? "fatal: Authentication failed" + : "fatal: Could not resolve host", + ), + ), + }); + }), + ); + const driver = yield* makeGitVcsDriverCore().pipe( + Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), + Effect.provide(ServerConfigLayer), + ); + const fetching = yield* driver + .fetchRemote({ cwd, remoteName: "origin", refName: "main" }) + .pipe(Effect.result, Effect.forkChild({ startImmediately: true })); + yield* Deferred.await(started); + if (failure === "timeout") { + yield* TestClock.adjust("31 seconds"); + yield* TestClock.adjust("31 seconds"); + } + const result = yield* Fiber.join(fetching); + assert.isTrue(Result.isFailure(result)); + assert.equal(attempts.length, 1); + if (Result.isFailure(result)) { + assert.equal( + result.failure.detail, + failure === "timeout" + ? "Git command timed out." + : failure === "auth" + ? "Git could not authenticate with the remote. Check Git credentials or SSH access on the server, then retry." + : "Git could not reach the remote. Check the server's network connection and remote host, then retry.", + ); + } + }), + ); + } + it.effect("explains a real fetch failure for a missing local remote", () => Effect.gen(function* () { const cwd = yield* makeTmpDir(); @@ -2727,7 +2839,15 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { assert.notEqual(beforeFetch, remoteHead); const driver = yield* GitVcsDriver.GitVcsDriver; - yield* driver.fetchRemote({ cwd, remoteName: "origin" }); + yield* git(peer, ["push", "origin", "HEAD:refs/heads/unrelated"]); + yield* driver.fetchRemote({ + cwd, + remoteName: "origin", + refName: initialBranch, + }); + assert.isFalse( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "unrelated" }), + ); assert.equal( yield* driver.remoteBranchExists({ @@ -2789,6 +2909,46 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { const status = yield* driver.statusDetails(worktreePath); assert.equal(status.aheadCount, 0); assert.equal(status.aheadOfDefaultCount, 0); + + // Local-only bases still use the old full-fetch fallback. + yield* driver.fetchRemote({ cwd, remoteName: "origin", refName: "local-only" }); + assert.isTrue( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "unrelated" }), + ); + + // A branch literally named origin/topic must retain the old lookup at + // refs/remotes/origin/origin/topic, not get stripped to origin/topic. + yield* git(peer, ["push", "origin", "HEAD:refs/heads/origin/topic"]); + yield* driver.fetchRemote({ cwd, remoteName: "origin", refName: "origin/topic" }); + assert.isTrue( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "origin/topic" }), + ); + + // A custom no-force fetch refspec keeps its full-fetch semantics. + yield* git(peer, ["push", "origin", "HEAD:refs/heads/custom-policy"]); + yield* git(cwd, ["config", "remote.origin.fetch", "refs/heads/*:refs/remotes/origin/*"]); + yield* driver.fetchRemote({ cwd, remoteName: "origin", refName: initialBranch }); + assert.isTrue( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "custom-policy" }), + ); + + // Pruning needs the full remote refspec, not only the selected branch. + yield* git(cwd, ["config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"]); + yield* git(cwd, ["config", "fetch.prune", "true"]); + yield* git(peer, ["push", "origin", ":refs/heads/custom-policy"]); + yield* driver.fetchRemote({ cwd, remoteName: "origin", refName: initialBranch }); + assert.isFalse( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "custom-policy" }), + ); + + // A remote configured to fetch all tags also keeps its full branch fetch. + yield* git(cwd, ["config", "--unset", "fetch.prune"]); + yield* git(cwd, ["config", "remote.origin.tagOpt", "--tags"]); + yield* git(peer, ["push", "origin", "HEAD:refs/heads/tag-policy"]); + yield* driver.fetchRemote({ cwd, remoteName: "origin", refName: initialBranch }); + assert.isTrue( + yield* driver.remoteBranchExists({ cwd, remoteName: "origin", refName: "tag-policy" }), + ); }), ); diff --git a/apps/server/src/vcs/GitVcsDriverCore.ts b/apps/server/src/vcs/GitVcsDriverCore.ts index ebae13509a..d476a37ebc 100644 --- a/apps/server/src/vcs/GitVcsDriverCore.ts +++ b/apps/server/src/vcs/GitVcsDriverCore.ts @@ -3074,23 +3074,32 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* const progress = options?.progress; const onCheckoutProgress = progress?.onCheckoutProgress; - yield* executeGit("GitVcsDriver.createWorktree", input.cwd, args, { - fallbackErrorDetail: "git worktree add failed", - timeoutMs: WORKTREE_ADD_TIMEOUT_MS, - ...(onCheckoutProgress - ? { - // Git only prints checkout progress when stderr is a tty or the - // delay elapsed. GIT_PROGRESS_DELAY=0 forces it through the pipe. - env: { GIT_PROGRESS_DELAY: "0", LC_ALL: "C" }, - progress: { - onStderrLine: (line) => { - const parsed = parseGitCheckoutProgressLine(line); - return parsed ? onCheckoutProgress(parsed) : Effect.void; + // Git defaults to a single checkout worker unless the caller opts in. + // Respect an explicit checkout.workers setting while enabling Git's + // automatic worker count for the common unset case. + const checkoutWorkers = (yield* readConfigValue(input.cwd, "checkout.workers")) ?? "0"; + yield* executeGit( + "GitVcsDriver.createWorktree", + input.cwd, + ["-c", `checkout.workers=${checkoutWorkers}`, ...args], + { + fallbackErrorDetail: "git worktree add failed", + timeoutMs: WORKTREE_ADD_TIMEOUT_MS, + ...(onCheckoutProgress + ? { + // Git only prints checkout progress when stderr is a tty or the + // delay elapsed. GIT_PROGRESS_DELAY=0 forces it through the pipe. + env: { GIT_PROGRESS_DELAY: "0", LC_ALL: "C" }, + progress: { + onStderrLine: (line) => { + const parsed = parseGitCheckoutProgressLine(line); + return parsed ? onCheckoutProgress(parsed) : Effect.void; + }, }, - }, - } - : {}), - }); + } + : {}), + }, + ); if (progress?.onWorktreeClaimed) { yield* progress.onWorktreeClaimed(worktreePath); @@ -3302,28 +3311,89 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* env: STATUS_UPSTREAM_REFRESH_ENV, fallbackErrorDetail: `git fetch ${input.remoteName} failed`, }; - yield* executeGitWithStableDiagnostics("GitVcsDriver.fetchRemote", input.cwd, args, { - ...options, - allowNonZeroExit: true, - }).pipe( - Effect.flatMap((result) => - result.exitCode === 0 - ? Effect.void - : Effect.fail( - new GitCommandError({ - ...gitCommandContext({ - operation: "GitVcsDriver.fetchRemote", - cwd: input.cwd, - args, - }), - detail: fetchFailureDetail(result.stderr) ?? options.fallbackErrorDetail, - ...(result.exitCode === null ? {} : { exitCode: result.exitCode }), - stdoutLength: result.stdout.length, - stderrLength: result.stderr.length, - }), - ), - ), - ); + const runFetch = (fetchArgs: ReadonlyArray) => + executeGitWithStableDiagnostics("GitVcsDriver.fetchRemote", input.cwd, [...fetchArgs], { + ...options, + allowNonZeroExit: true, + }); + let fetchArgs: ReadonlyArray = args; + let result; + // An explicit refspec must not silently override a repository's custom + // remote.fetch mapping or its force-update policy. Scope the common + // default mapping; keep the existing full fetch for all other setups. + const defaultFetchRefspec = `+refs/heads/*:refs/remotes/${input.remoteName}/*`; + const configuredFetchRefspecs = input.refName + ? (yield* runGitStdout( + "GitVcsDriver.fetchRemote.readRefspecs", + input.cwd, + ["config", "--get-all", `remote.${input.remoteName}.fetch`], + true, + )) + .split(/\r?\n/) + .filter((line) => line.length > 0) + : []; + const pruneSettings = input.refName + ? yield* Effect.forEach( + [ + "fetch.prune", + "fetch.pruneTags", + `remote.${input.remoteName}.prune`, + `remote.${input.remoteName}.pruneTags`, + ], + (key) => + runGitStdout( + "GitVcsDriver.fetchRemote.readPruneSetting", + input.cwd, + ["config", "--type=bool", "--get", key], + true, + ).pipe(Effect.map((value) => value.trim() === "true")), + ) + : []; + const tagOption = input.refName + ? yield* readConfigValue(input.cwd, `remote.${input.remoteName}.tagOpt`) + : null; + if ( + input.refName && + parseRemoteRefWithRemoteNames(input.refName, [input.remoteName]) === null && + configuredFetchRefspecs.length === 1 && + configuredFetchRefspecs[0] === defaultFetchRefspec && + !pruneSettings.some(Boolean) && + tagOption !== "--tags" + ) { + const branch = input.refName; + const scopedArgs = [ + ...args, + `+refs/heads/${branch}:refs/remotes/${input.remoteName}/${branch}`, + ]; + fetchArgs = scopedArgs; + result = yield* runFetch(scopedArgs); + // A local-only base has no matching remote branch. Preserve the old + // full-fetch behavior so callers can still discover other remote refs. + if ( + result.exitCode !== 0 && + result.stderr + .split(/\r?\n/) + .includes(`fatal: couldn't find remote ref refs/heads/${branch}`) + ) { + fetchArgs = args; + result = yield* runFetch(args); + } + } else { + result = yield* runFetch(args); + } + if (result.exitCode !== 0) { + return yield* new GitCommandError({ + ...gitCommandContext({ + operation: "GitVcsDriver.fetchRemote", + cwd: input.cwd, + args: fetchArgs, + }), + detail: fetchFailureDetail(result.stderr) ?? options.fallbackErrorDetail, + ...(result.exitCode === null ? {} : { exitCode: result.exitCode }), + stdoutLength: result.stdout.length, + stderrLength: result.stderr.length, + }); + } }, ); diff --git a/apps/server/src/ws.ts b/apps/server/src/ws.ts index 2f34051340..fca63f651b 100644 --- a/apps/server/src/ws.ts +++ b/apps/server/src/ws.ts @@ -1382,6 +1382,7 @@ const makeWsRpcLayer = ( yield* gitWorkflow.fetchRemote({ cwd: prepareWorktree.projectCwd, remoteName: "origin", + refName: prepareWorktree.baseBranch, }); const remoteBaseExists = yield* gitWorkflow.remoteBranchExists({ cwd: prepareWorktree.projectCwd, From 47f9f165bff4696b74e5b053ed203a15a9a72b9b Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:29:16 -0600 Subject: [PATCH 038/104] fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture --- .../src/wsl/DesktopWslEnvironment.test.ts | 87 ++++++++++++++++--- apps/desktop/src/wsl/DesktopWslEnvironment.ts | 57 ++++++++++-- 2 files changed, 128 insertions(+), 16 deletions(-) diff --git a/apps/desktop/src/wsl/DesktopWslEnvironment.test.ts b/apps/desktop/src/wsl/DesktopWslEnvironment.test.ts index 8301c9ea33..fb360e2f63 100644 --- a/apps/desktop/src/wsl/DesktopWslEnvironment.test.ts +++ b/apps/desktop/src/wsl/DesktopWslEnvironment.test.ts @@ -168,7 +168,7 @@ describe("WSL runtime cache", () => { expect(nightly).not.toContain('"$HOME/.pylon-code/wsl-runtime"'); expect(buildWslRuntimeInvalidateScript("sha256-bbb", "pylon-code-nightly")).toContain( - ".pylon-code-nightly/wsl-runtime/sha256-bbb", + ".pylon-code-nightly/wsl-runtime", ); }); @@ -190,6 +190,10 @@ describe("WSL runtime cache", () => { expect(script).toContain("trap 'exit 1' HUP INT TERM"); expect(script).toContain('exec 9> "$runtime_lock"'); expect(script).toContain("flock -x 9"); + expect(script).toContain('probe_failure_marker="$runtime_parent/.1.2.3-x64.probe-failed"'); + expect(script.indexOf('if [ -f "$probe_failure_marker" ]')).toBeLessThan( + script.indexOf("if runtime_is_ready; then"), + ); expect(script).not.toContain("runtime_lock_pid"); expect(script).not.toContain("sleep 0.1"); expect(script).not.toContain('rm -rf "$runtime_lock"'); @@ -386,7 +390,7 @@ describe("WSL runtime cache", () => { // Without visible processes the retention rules cannot tell a live cache // from an abandoned one, so the sweep is skipped rather than guessed at. - expect(script).toContain("[ -d /proc/1 ] || exit 0"); + expect(script).toContain("[ -d /proc/1 ] && have_process_info=1"); // The guard has to gate the delete, not just exist. const inUseChecked = script.indexOf('! runtime_in_use "$candidate"'); @@ -400,9 +404,9 @@ describe("WSL runtime cache", () => { // Dot-prefixed, so `"$runtime_parent"/*` never matches them, and they carry // no ready marker either; without this pass a killed install leaks forever. - expect(script).toContain( - 'for scratch in "$runtime_parent"/.*.tmp.* "$runtime_parent"/.*.stale.*; do', - ); + expect(script).toContain('for scratch in "$runtime_parent"/.*.tmp.*; do'); + expect(script).toContain('for scratch in "$runtime_parent"/.*.stale.*; do'); + expect(script).toContain('! runtime_in_use "$runtime_parent/$original_name" || continue'); // Age guard: a scratch directory younger than this belongs to a live install. expect(script).toContain('find "$scratch" -maxdepth 0 -mmin +120'); }); @@ -412,9 +416,8 @@ describe("WSL runtime cache", () => { // Readiness is a presence check, so a tree whose pty.node is present but // unloadable stays ready forever unless the probe can revoke the marker. - expect(script).toContain( - 'rm -f "$HOME/.pylon-code/wsl-runtime/1.2.3_x64/.t3code-wsl-runtime-ready"', - ); + expect(script).toContain('rm -f "$runtime_parent/1.2.3_x64/.t3code-wsl-runtime-ready"'); + expect(script).toContain('touch "$runtime_parent/.1.2.3_x64.probe-failed"'); // Deleting the tree here would pull it out from under any backend still // running from it; the next install moves an unready root aside instead. expect(script).not.toContain("rm -rf"); @@ -503,6 +506,34 @@ describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed expect(restored.stdout).toBe(SERVER_ENTRY_SOURCE); }); + it("backs off a proven native probe failure and retries after the cooldown", () => { + const fixture = createFixture(); + expect(fixture.install().status).toBe(0); + const invalidated = runShell( + [ + "set -eu", + `HOME=${sh(`${fixture.work}/home`)}`, + "export HOME", + buildWslRuntimeInvalidateScript(fixture.runtimeId, "pylon-code"), + ].join("\n"), + ); + expect(invalidated.status, invalidated.stderr).toBe(0); + + // No extraction or runtime path is offered during the cooling period. + const cooled = fixture.install(); + expect(cooled.status).toBe(2); + expect(parseWslRuntimeRoot(cooled.stdout)).toBeNull(); + expect(cooled.stderr).toContain("using the mounted tree"); + + const expired = runShell( + `set -eu\ntouch -d "11 minutes ago" ${sh(`${fixture.runtimeParent}/.${fixture.runtimeId}.probe-failed`)}`, + ); + expect(expired.status, expired.stderr).toBe(0); + const repaired = fixture.install(); + expect(repaired.status, repaired.stderr).toBe(0); + expect(parseWslRuntimeRoot(repaired.stdout)).toBe(fixture.runtimeRoot); + }); + it("falls back instead of launching a corrupted cache it cannot reinstall", () => { const fixture = createFixture(); expect(fixture.install().status).toBe(0); @@ -664,7 +695,7 @@ describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); }); - it("removes an aged stale tree after replacing an active unready cache", () => { + it("keeps an aged stale tree until its original runtime process exits", () => { const fixture = createFixture(); expect(fixture.install().status).toBe(0); const result = runShell( @@ -683,15 +714,51 @@ describe.skipIf(posixShellRunner === null)("WSL runtime install script (executed `HOME=${sh(`${fixture.work}/home`)}`, "export HOME", buildWslRuntimePruneScript(fixture.runtimeId, "pylon-code"), - 'test ! -e "$stale"', + 'test -d "$stale"', "kill $active_pid", "wait $active_pid 2>/dev/null || true", + buildWslRuntimePruneScript(fixture.runtimeId, "pylon-code"), + 'test ! -e "$stale"', ].join("\n"), ); expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); }); + it("does not sweep aged extraction scratch while its install lock is held", () => { + const fixture = createFixture(); + const result = runShell( + [ + "set -eu", + `runtime_parent=${sh(fixture.runtimeParent)}`, + 'mkdir -p "$runtime_parent"', + `held_scratch="$runtime_parent/.${fixture.runtimeId}.tmp.test"`, + 'mkdir -p "$held_scratch"', + 'touch -d "180 minutes ago" "$held_scratch"', + // The writer signals only after flock has acquired the lock. -F and + // exec keep $! as the exact lock-owning sleep process for cleanup. + 'lock_ready="$runtime_parent/lock-ready.pipe"', + 'mkfifo "$lock_ready"', + `flock -x -F "$runtime_parent/.${fixture.runtimeId}.install.lock" sh -c 'printf "ready\\n" > "$1"; exec sleep 30' sh "$lock_ready" >/dev/null 2>&1 &`, + "lock_pid=$!", + 'trap \'kill "$lock_pid" 2>/dev/null || true; wait "$lock_pid" 2>/dev/null || true\' EXIT', + 'locked=$(timeout 5 cat "$lock_ready")', + 'test "$locked" = ready', + 'rm "$lock_ready"', + `HOME=${sh(`${fixture.work}/home`)}`, + "export HOME", + buildWslRuntimePruneScript(fixture.runtimeId, "pylon-code"), + 'test -d "$held_scratch"', + "kill $lock_pid", + "wait $lock_pid 2>/dev/null || true", + "trap - EXIT", + buildWslRuntimePruneScript(fixture.runtimeId, "pylon-code"), + 'test ! -e "$held_scratch"', + ].join("\n"), + ); + expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); + }); + it("prunes old and markerless caches without touching retained, active, locked, or unrelated roots", () => { const result = runShell( [ diff --git a/apps/desktop/src/wsl/DesktopWslEnvironment.ts b/apps/desktop/src/wsl/DesktopWslEnvironment.ts index 3adf7d2498..f9c18a01ec 100644 --- a/apps/desktop/src/wsl/DesktopWslEnvironment.ts +++ b/apps/desktop/src/wsl/DesktopWslEnvironment.ts @@ -22,7 +22,9 @@ const WSLPATH_TIMEOUT = Duration.seconds(10); const PROBE_TIMEOUT = Duration.seconds(10); const TOOLCHAIN_TIMEOUT = Duration.seconds(10); const BUILD_TIMEOUT = Duration.minutes(5); -const RUNTIME_INSTALL_TIMEOUT = Duration.minutes(2); +// A cold extraction reads the archive from /mnt/c while holding an install lock. +// Slow disks and antivirus scans can take longer than two minutes. +const RUNTIME_INSTALL_TIMEOUT = Duration.minutes(10); const RUNTIME_PRUNE_TIMEOUT = Duration.seconds(30); const RUNTIME_INVALIDATE_TIMEOUT = Duration.seconds(15); const USER_HOME_TIMEOUT = Duration.seconds(5); @@ -261,6 +263,7 @@ const shellQuote = (value: string): string => `'${value.replaceAll("'", "'\\''") // that install wrote. const WSL_RUNTIME_READY_MARKER = ".t3code-wsl-runtime-ready"; const WSL_RUNTIME_SELECTED_MARKER = ".t3code-wsl-runtime-selected"; +const WSL_RUNTIME_PROBE_FAILURE_COOLDOWN_MINUTES = 10; // Pylon's own runtime home inside the distro, per channel — not `~/.t3`, which // belongs to T3 Code. This is a correctness boundary rather than branding: // `pruneRuntimes` deletes every `sha256-*` sibling it does not recognise as @@ -330,9 +333,18 @@ export const buildWslRuntimeInstallScript = ( "}", 'mkdir -p "$runtime_parent"', `runtime_lock="$runtime_parent/.${safeRuntimeId}.install.lock"`, + `probe_failure_marker="$runtime_parent/.${safeRuntimeId}.probe-failed"`, "trap 'exit 1' HUP INT TERM", 'exec 9> "$runtime_lock"', "flock -x 9", + // A staged binary that failed a real node-pty load while the mounted tree + // worked is unlikely to be repaired by reinstalling identical archive + // bytes on every desktop launch. Retry after a bounded cooling period. + `if [ -f "$probe_failure_marker" ] && find "$probe_failure_marker" -maxdepth 0 -mmin -${WSL_RUNTIME_PROBE_FAILURE_COOLDOWN_MINUTES} -print -quit | grep -q .; then`, + " printf 'The staged WSL runtime failed its native probe recently; using the mounted tree until retry.\\n' >&2", + " exit 2", + "fi", + 'rm -f -- "$probe_failure_marker"', "if runtime_is_ready; then", ` touch "$runtime_root/${WSL_RUNTIME_SELECTED_MARKER}"`, ` printf 'runtimeRoot:%s\\n' "$runtime_root"`, @@ -436,13 +448,16 @@ export const buildWslRuntimePruneScript = ( 'prune_lock="$runtime_parent/.prune.lock"', 'exec 8> "$prune_lock"', "flock -x 8", - // Without a way to see the distro's processes we cannot tell which caches - // are load-bearing, and the retention rules below are not safe on their own. - "[ -d /proc/1 ] || exit 0", + // Old extraction scratch can be swept without process info only after + // checking its stable per-runtime install lock. A timed-out host call does + // not prove that WSL's tar process has exited. + "have_process_info=0", + "[ -d /proc/1 ] && have_process_info=1", "runtime_in_use() {", ' grep -qF -- "$1/" /proc/[0-9]*/cmdline 2>/dev/null', "}", 'previous_runtime=""', + 'if [ "$have_process_info" = 1 ]; then', 'for candidate in "$runtime_parent"/sha256-*; do', ' [ -d "$candidate" ] || continue', ' [ "$candidate" != "$current_runtime" ] || continue', @@ -470,14 +485,38 @@ export const buildWslRuntimePruneScript = ( " continue", " fi", ' rm -rf -- "$candidate"', + // Keep the per-runtime flock pathname. Removing it after unlocking can + // split concurrent installers across old and new inodes. These tiny files + // are stable coordination state, even after their cache tree is pruned. " flock -u 9", "done", + "fi", // Interrupted installs use dot-prefixed names under this dedicated parent. - 'for scratch in "$runtime_parent"/.*.tmp.* "$runtime_parent"/.*.stale.*; do', + 'for scratch in "$runtime_parent"/.*.tmp.*; do', ' [ -d "$scratch" ] || continue', ` find "$scratch" -maxdepth 0 -mmin +${String(ORPHANED_RUNTIME_SCRATCH_MAX_AGE_MINUTES)} -print -quit | grep -q . || continue`, + " scratch_name=${scratch##*/}", + " scratch_runtime=${scratch_name#.}", + " scratch_runtime=${scratch_runtime%%.tmp.*}", + ' exec 9> "$runtime_parent/.${scratch_runtime}.install.lock"', + " flock -n 9 || continue", ' rm -rf -- "$scratch"', + " flock -u 9", "done", + 'if [ "$have_process_info" = 1 ]; then', + ' for scratch in "$runtime_parent"/.*.stale.*; do', + ' [ -d "$scratch" ] || continue', + ` find "$scratch" -maxdepth 0 -mmin +${String(ORPHANED_RUNTIME_SCRATCH_MAX_AGE_MINUTES)} -print -quit | grep -q . || continue`, + " stale_name=${scratch##*/}", + " original_name=${stale_name#.}", + " original_name=${original_name%%.stale.*}", + ' ! runtime_in_use "$runtime_parent/$original_name" || continue', + ' exec 9> "$runtime_parent/.${original_name}.install.lock"', + " flock -n 9 || continue", + ' rm -rf -- "$scratch"', + " flock -u 9", + " done", + "fi", ].join("\n"); }; @@ -495,7 +534,13 @@ export const buildWslRuntimeInvalidateScript = ( const safeRuntimeId = sanitizeWslRuntimeId(runtimeId); return [ "set -eu", - `rm -f "$HOME/.${runtimeHomeDirName}/wsl-runtime/${safeRuntimeId}/${WSL_RUNTIME_READY_MARKER}"`, + `runtime_parent=${wslRuntimeParent(runtimeHomeDirName)}`, + `[ -d "$runtime_parent" ] || exit 0`, + `runtime_lock="$runtime_parent/.${safeRuntimeId}.install.lock"`, + 'exec 9> "$runtime_lock"', + "flock -x 9", + `rm -f "$runtime_parent/${safeRuntimeId}/${WSL_RUNTIME_READY_MARKER}"`, + `touch "$runtime_parent/.${safeRuntimeId}.probe-failed"`, ].join("\n"); }; From 66396e0031e052deb05be0ab130d46d430dc43e5 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:29:32 -0600 Subject: [PATCH 039/104] fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 --- apps/desktop/src/app/DesktopApp.ts | 33 ++-- .../src/backend/DesktopBackendManager.test.ts | 145 ++++++++++++++++++ .../src/backend/DesktopBackendManager.ts | 13 +- 3 files changed, 177 insertions(+), 14 deletions(-) diff --git a/apps/desktop/src/app/DesktopApp.ts b/apps/desktop/src/app/DesktopApp.ts index 719502636b..b5a7bb2007 100644 --- a/apps/desktop/src/app/DesktopApp.ts +++ b/apps/desktop/src/app/DesktopApp.ts @@ -1,4 +1,5 @@ import * as Cause from "effect/Cause"; +import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Ref from "effect/Ref"; @@ -142,6 +143,20 @@ const handleFatalStartupError = Effect.fn("desktop.startup.handleFatalStartupErr const fatalStartupCause = (stage: string, cause: Cause.Cause) => handleFatalStartupError(stage, Cause.pretty(cause)).pipe(Effect.andThen(Effect.failCause(cause))); +export const stopAllPoolInstances = Effect.fn("desktop.app.stopAllPoolInstances")( + function* (): Effect.fn.Return { + // Stop every backend in the pool with a timeout to guarantee the quit + // path makes progress even if a backend hangs during teardown. + const pool = yield* DesktopBackendPool.DesktopBackendPool; + const instances = yield* pool.list; + yield* Effect.forEach( + instances, + (instance) => instance.stop({ timeout: Duration.seconds(5) }), + { concurrency: "unbounded" }, + ); + }, +); + const bootstrap = Effect.gen(function* () { const pool = yield* DesktopBackendPool.DesktopBackendPool; const primaryBackend = yield* pool.primary; @@ -313,18 +328,12 @@ const scopedProgram = Effect.scoped( const shutdown = yield* DesktopShutdown.DesktopShutdown; yield* Effect.addFinalizer(() => - Effect.gen(function* () { - const pool = yield* DesktopBackendPool.DesktopBackendPool; - // Stop every backend in the pool, not just the primary. The - // electronApp.quit() path can race ahead of the layer-scope - // cascade, so leaving the WSL instance for its parent scope - // finalizer means it gets hard-killed by the OS instead of - // receiving SIGTERM + grace. Stops run concurrently. - const instances = yield* pool.list; - yield* Effect.forEach(instances, (instance) => instance.stop(), { - concurrency: "unbounded", - }); - }).pipe(Effect.ensuring(shutdown.markComplete)), + // Stop every backend in the pool, not just the primary. The + // electronApp.quit() path can race ahead of the layer-scope + // cascade, so leaving the WSL instance for its parent scope + // finalizer means it gets hard-killed by the OS instead of + // receiving SIGTERM + grace. + stopAllPoolInstances().pipe(Effect.ensuring(shutdown.markComplete)), ); yield* startup; diff --git a/apps/desktop/src/backend/DesktopBackendManager.test.ts b/apps/desktop/src/backend/DesktopBackendManager.test.ts index 53ccf5a756..4bbecd7d74 100644 --- a/apps/desktop/src/backend/DesktopBackendManager.test.ts +++ b/apps/desktop/src/backend/DesktopBackendManager.test.ts @@ -23,6 +23,8 @@ import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstab import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as DesktopBackendManager from "./DesktopBackendManager.ts"; +import * as DesktopApp from "../app/DesktopApp.ts"; +import * as DesktopBackendPool from "./DesktopBackendPool.ts"; import * as DesktopObservability from "../app/DesktopObservability.ts"; import * as DesktopTelemetryPublisher from "../telemetry/DesktopTelemetryPublisher.ts"; import * as DesktopWslEnvironment from "../wsl/DesktopWslEnvironment.ts"; @@ -1503,4 +1505,147 @@ describe("DesktopBackendManager", () => { }).pipe(Effect.provide(TestClock.layer())), ), ); + + it.effect("stopAllPoolInstances bounds the quit finalizer when backends hang", () => + Effect.scoped( + Effect.gen(function* () { + // Each backend's process-scope finalizer reports when it starts and + // when it finishes, keyed by instance name, so the test can prove + // both backends reached each milestone instead of inferring it from + // a shared flag or a clock advance. + const teardownStarted = yield* Queue.unbounded(); + const teardownFinished = yield* Queue.unbounded(); + const allowTeardown = yield* Deferred.make(); + + const makeInstance = (name: string) => + makeTestInstance({ + spawnerLayer: Layer.succeed( + ChildProcessSpawner.ChildProcessSpawner, + ChildProcessSpawner.make(() => + Effect.gen(function* () { + const scope = yield* Scope.Scope; + yield* Scope.addFinalizer( + scope, + Queue.offer(teardownStarted, name).pipe( + Effect.andThen(Deferred.await(allowTeardown)), + Effect.andThen(Queue.offer(teardownFinished, name)), + Effect.asVoid, + ), + ); + return makeProcess({ exitCode: Effect.never }); + }), + ), + ), + httpClientLayer: httpClientLayer(() => Effect.never), + }); + + const instance1 = yield* makeInstance("instance1"); + const instance2 = yield* makeInstance("instance2"); + + yield* instance1.start; + yield* instance2.start; + + const mockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { + list: Effect.succeed([instance1, instance2]), + get: () => Effect.succeed(Option.none()), + primary: Effect.die(new Error("primary not implemented")), + register: () => Effect.die(new Error("register not implemented")), + unregister: () => Effect.die(new Error("unregister not implemented")), + }); + + // Mirror the quit path: register stopAllPoolInstances as a scope + // finalizer and let the scope close run it, rather than calling it + // as an ordinary interruptible effect. + const quitFiber = yield* Effect.scoped( + Effect.addFinalizer(() => DesktopApp.stopAllPoolInstances()), + ).pipe(Effect.provide(mockPool), Effect.forkChild); + + const started = yield* Queue.takeN(teardownStarted, 2); + assert.deepEqual(started.toSorted(), ["instance1", "instance2"]); + + // Both backends are now hung in teardown. Advancing past the 5s + // budget must let the quit finalizer return without them. + yield* TestClock.adjust(Duration.seconds(5)); + yield* Fiber.join(quitFiber); + assert.equal(yield* Queue.size(teardownFinished), 0); + + // The timed-out closes keep running in the background and finish + // once the backends unblock. + yield* Deferred.succeed(allowTeardown, undefined); + const finished = yield* Queue.takeN(teardownFinished, 2); + assert.deepEqual(finished.toSorted(), ["instance1", "instance2"]); + }).pipe(Effect.provide(TestClock.layer())), + ), + ); + + it.effect("stopAllPoolInstances bounds an early shutdown callback", () => + Effect.scoped( + Effect.gen(function* () { + const ready = yield* Queue.unbounded(); + const hookStarted = yield* Queue.unbounded(); + const hookFinished = yield* Queue.unbounded(); + const releaseHook = yield* Deferred.make(); + + const makeInstance = (name: string) => + makeTestInstance({ + spawnerLayer: Layer.succeed( + ChildProcessSpawner.ChildProcessSpawner, + ChildProcessSpawner.make(() => + Effect.gen(function* () { + const scope = yield* Scope.Scope; + const closed = yield* Deferred.make(); + yield* Scope.addFinalizer( + scope, + Deferred.succeed(closed, undefined).pipe(Effect.asVoid), + ); + return makeProcess({ + exitCode: Deferred.await(closed).pipe( + Effect.as(ChildProcessSpawner.ExitCode(0)), + ), + kill: () => Deferred.succeed(closed, undefined).pipe(Effect.asVoid), + }); + }), + ), + ), + onReady: Queue.offer(ready, name).pipe(Effect.asVoid), + onShutdown: Queue.offer(hookStarted, name).pipe( + Effect.andThen(Deferred.await(releaseHook)), + Effect.andThen(Queue.offer(hookFinished, name)), + Effect.asVoid, + ), + }); + + const instance1 = yield* makeInstance("instance1"); + const instance2 = yield* makeInstance("instance2"); + yield* instance1.start; + yield* instance2.start; + assert.deepEqual((yield* Queue.takeN(ready, 2)).toSorted(), ["instance1", "instance2"]); + + const mockPool = Layer.succeed(DesktopBackendPool.DesktopBackendPool, { + list: Effect.succeed([instance1, instance2]), + get: () => Effect.succeed(Option.none()), + primary: Effect.die(new Error("primary not implemented")), + register: () => Effect.die(new Error("register not implemented")), + unregister: () => Effect.die(new Error("unregister not implemented")), + }); + const quitFiber = yield* Effect.scoped( + Effect.addFinalizer(() => DesktopApp.stopAllPoolInstances()), + ).pipe(Effect.provide(mockPool), Effect.forkChild); + + assert.deepEqual((yield* Queue.takeN(hookStarted, 2)).toSorted(), [ + "instance1", + "instance2", + ]); + yield* TestClock.adjust(Duration.seconds(5)); + yield* Fiber.join(quitFiber); + assert.equal(yield* Queue.size(hookFinished), 0); + + yield* Deferred.succeed(releaseHook, undefined); + assert.deepEqual((yield* Queue.takeN(hookFinished, 2)).toSorted(), [ + "instance1", + "instance2", + ]); + }).pipe(Effect.provide(TestClock.layer())), + ), + ); }); diff --git a/apps/desktop/src/backend/DesktopBackendManager.ts b/apps/desktop/src/backend/DesktopBackendManager.ts index 6f1ea139bd..997f6df459 100644 --- a/apps/desktop/src/backend/DesktopBackendManager.ts +++ b/apps/desktop/src/backend/DesktopBackendManager.ts @@ -1045,7 +1045,7 @@ export const makeBackendInstance = Effect.fn("makeBackendInstance")(function* ( }); }); - const stop = Effect.fn("desktop.backendInstance.stop")(function* (options?: { + const stopUnsafe = Effect.fn("desktop.backendInstance.stopUnsafe")(function* (options?: { readonly timeout?: Duration.Duration; }) { const { active, restartFiber, notifyShutdown } = yield* mutex.withPermits(1)( @@ -1137,6 +1137,15 @@ export const makeBackendInstance = Effect.fn("makeBackendInstance")(function* ( }); }); + const stop: DesktopBackendInstance["stop"] = (options) => { + const timeout = options?.timeout; + if (timeout === undefined) return stopUnsafe(); + return Effect.forkIn(stopUnsafe(), parentScope).pipe( + Effect.flatMap((fiber) => Fiber.join(fiber).pipe(Effect.timeoutOption(timeout))), + Effect.asVoid, + ); + }; + const waitForReady = (timeout: Duration.Duration): Effect.Effect => Effect.gen(function* () { const current = yield* Ref.get(state); @@ -1154,7 +1163,7 @@ export const makeBackendInstance = Effect.fn("makeBackendInstance")(function* ( Effect.map(Option.getOrElse(() => false)), ); - yield* Effect.addFinalizer(() => stop()); + yield* Effect.addFinalizer(() => stopUnsafe({ timeout: Duration.seconds(5) })); return { id: spec.id, From dae8134b79b2e4694b089b72d761cbb45879709a Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:31:21 -0600 Subject: [PATCH 040/104] docs(claude): clarify OpenRouter model selection (#816) --- docs/user/providers-claude.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/user/providers-claude.md b/docs/user/providers-claude.md index 190f7591bd..5c3b442f39 100644 --- a/docs/user/providers-claude.md +++ b/docs/user/providers-claude.md @@ -132,9 +132,16 @@ Use `https://openrouter.ai/api`, not `/api/v1`. If that Claude config directory login, run `/logout` in a Claude Code session using that directory before starting the router setup. Cached login credentials can conflict with the router token. -Verify requests with `/status` in a Claude session or in OpenRouter's activity dashboard. For -model-role overrides and current compatibility requirements, use the -[OpenRouter Claude Code guide](https://openrouter.ai/docs/cookbook/coding-agents/claude-code-integration). +Choose the model in Pylon's chat model picker. If the OpenRouter model is not listed, open that +Claude instance in **Settings → Providers**, add its full model ID with **Add custom model**, then +select it in the picker. Claude Code's `ANTHROPIC_DEFAULT_*_MODEL` variables map role aliases such +as `sonnet`; they do not replace a model ID explicitly selected in Pylon. Custom models may expose +fewer effort, thinking, or context controls than built-in models. + +Verify the router connection with `/status` in a Claude session using that config directory, and +check OpenRouter's activity dashboard for the model used by Pylon. For model-role overrides and +current compatibility requirements, use the +[OpenRouter Claude Code guide](https://openrouter.ai/docs/guides/coding-agents/claude-code-integration). ## Other routers From 65a80a3d6a22413d622eaa49260bc4afd4e65b5a Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:31:35 -0600 Subject: [PATCH 041/104] fix(web): label message copy action accurately (#779) --- apps/web/src/components/chat/MessageCopyButton.tsx | 4 ++-- apps/web/src/components/chat/MessagesTimeline.test.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/chat/MessageCopyButton.tsx b/apps/web/src/components/chat/MessageCopyButton.tsx index 4a5c3f170d..aaaa171f10 100644 --- a/apps/web/src/components/chat/MessageCopyButton.tsx +++ b/apps/web/src/components/chat/MessageCopyButton.tsx @@ -37,7 +37,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({ copyToClipboard(text)} ref={ref} @@ -51,7 +51,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({ {isCopied ? : } -

Copy to clipboard

+

Copy message

); diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx index 4afd75636f..86836e9229 100644 --- a/apps/web/src/components/chat/MessagesTimeline.test.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx @@ -1357,7 +1357,7 @@ describe("MessagesTimeline", () => { />, ); - expect(markup).toContain('aria-label="Copy link"'); + expect(markup).toContain('aria-label="Copy message"'); expect(markup).toContain('data-user-message-collapsed="true"'); expect(markup).toContain('data-user-message-footer="true"'); }); From 06c628b4d9f90eb1d837e6e21ddf888dd3639e56 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:31:47 -0600 Subject: [PATCH 042/104] fix(web): keep PR checkout command during detail loading (#785) --- .../pullRequest/PullRequestDetailPanel.tsx | 17 +++---- .../pullRequestDetail.logic.test.ts | 46 +++++++++++++++++++ .../pullRequest/pullRequestDetail.logic.ts | 17 +++++++ 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx index 0080f4348d..665f085d27 100644 --- a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx +++ b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx @@ -149,12 +149,11 @@ import { handoffPrompt, handoffReviewComments, latestPullRequestReviewOutcomes, - loadingPullRequestCheckoutCommand, isStackedPullRequestBase, pullRequestActionMenuHasGroup, pullRequestActionNeedsHostRefresh, pullRequestComposerTarget, - pullRequestCheckoutCommand, + panelPullRequestCheckoutCommand, pullRequestFindingKey, pullRequestHandoffLabels, PULL_REQUEST_MERGE_METHOD_LABELS, @@ -765,14 +764,12 @@ export function PullRequestDetailPanel({ repositoryUrl !== null ? new URL(`/${encodeURIComponent(detail.author.login)}`, repositoryUrl).toString() : null; - const checkoutCommand = detail - ? pullRequestCheckoutCommand( - detail.provider, - detail.number, - detail.headBranch, - detail.headRepositoryNameWithOwner, - ) - : null; + const checkoutCommand = panelPullRequestCheckoutCommand({ + reference, + identity: repositoryIdentity, + summary: handoffSummary, + headRepositoryNameWithOwner: detail?.headRepositoryNameWithOwner, + }); const onCheckoutCommandError = useCallback((error: Error) => { toastManager.add({ type: "error", diff --git a/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts b/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts index 57affb3076..9c0a93ea0d 100644 --- a/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts +++ b/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts @@ -29,6 +29,7 @@ import { isPullRequestVerdictStale, isStackedPullRequestBase, loadingPullRequestCheckoutCommand, + panelPullRequestCheckoutCommand, isThreadOwnPullRequest, latestPullRequestReviewOutcomes, newestPullRequestCommitAt, @@ -114,6 +115,51 @@ describe("pull request checkout commands", () => { ), ).toBeNull(); }); + + it("keeps a checkout command available from the list summary while detail loads", () => { + const pullRequest = reference("github.com"); + expect( + panelPullRequestCheckoutCommand({ + reference: pullRequest, + identity: undefined, + summary: { provider: "github", number: 42, headBranch: "topic" }, + }), + ).toBe("gh pr checkout 42"); + expect( + panelPullRequestCheckoutCommand({ + reference: pullRequest, + identity: undefined, + summary: null, + }), + ).toBe("gh pr checkout 42"); + }); + + it("does not invent a checkout command for an unknown host or incomplete Bitbucket detail", () => { + expect( + panelPullRequestCheckoutCommand({ + reference: reference("forge.example"), + identity: undefined, + summary: null, + }), + ).toBeNull(); + expect( + panelPullRequestCheckoutCommand({ + reference: reference("bitbucket.org"), + identity: undefined, + summary: { provider: "bitbucket", number: 42, headBranch: "topic" }, + }), + ).toBeNull(); + expect( + panelPullRequestCheckoutCommand({ + reference: reference("bitbucket.org"), + identity: undefined, + summary: { provider: "bitbucket", number: 42, headBranch: "topic" }, + headRepositoryNameWithOwner: "acme/web", + }), + ).toBe( + "git clone --single-branch --branch topic https://bitbucket.org/acme/web.git t3code-pr-42", + ); + }); }); const TIMELINE_SOURCE: Pick< diff --git a/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts b/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts index b34cab9b5c..70b161c9c7 100644 --- a/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts +++ b/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts @@ -145,6 +145,23 @@ export function loadingPullRequestCheckoutCommand( return pullRequestCheckoutCommand(provider, reference.number, ""); } +/** Keep the checkout affordance stable while richer PR detail is loading. */ +export function panelPullRequestCheckoutCommand(input: { + readonly reference: PullRequestRef; + readonly identity: RepositoryIdentity | null | undefined; + readonly summary: Pick | null; + readonly headRepositoryNameWithOwner?: string | null | undefined; +}): string | null { + return input.summary + ? pullRequestCheckoutCommand( + input.summary.provider, + input.summary.number, + input.summary.headBranch, + input.headRepositoryNameWithOwner, + ) + : loadingPullRequestCheckoutCommand(input.reference, input.identity); +} + /** Activity changes only when the same host resource reports a newer revision. */ export function shouldRefreshPullRequestActivity( previous: { readonly key: string; readonly updatedAt: string } | null, From b10720ff66436897c79e33426443c7d5890cfbab Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:34:12 -0600 Subject: [PATCH 043/104] fix(web): expose selected menu choices to assistive tech (#806) --- apps/web/src/components/DiffPanel.tsx | 108 +++++++++--------- .../src/components/files/FileBreadcrumbs.tsx | 77 ++++++++----- .../pullRequest/PullRequestCodeTab.tsx | 50 ++++---- 3 files changed, 128 insertions(+), 107 deletions(-) diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index d954a7ff69..547856a426 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -72,7 +72,8 @@ import { import { DropdownMenu, DropdownMenuContent, - DropdownMenuItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, @@ -644,6 +645,26 @@ export default function DiffPanel({ if (!routeThreadRef) return; useDiffPanelStore.getState().selectBranchBaseRef(routeThreadRef, baseRef); }; + // The scope menu has two radio groups: the top-level one treats the latest + // turn as "latest", while the turn sub-menu keys every turn by id so the + // latest turn is also marked there. + const selectedTurnValue = selectedTurn ? `turn:${selectedTurn.turnId}` : ""; + const selectedScopeValue = + selectedTurnId === null + ? selectedGitScope + : selectedTurn?.turnId === latestTurn?.turnId + ? "latest" + : selectedTurnValue; + const selectScopeValue = (value: string) => { + if (value === "unstaged" || value === "branch") { + selectGitScope(value); + } else if (value === "latest") { + if (latestTurn) selectTurn(latestTurn.turnId); + } else { + const turn = orderedTurnDiffSummaries.find((summary) => `turn:${summary.turnId}` === value); + if (turn) selectTurn(turn.turnId); + } + }; const headerRow = ( <> @@ -657,61 +678,42 @@ export default function DiffPanel({ - selectGitScope("unstaged")} - > - Working tree - - selectGitScope("branch")} - > - Branch changes - - { - if (latestTurn) selectTurn(latestTurn.turnId); - }} - > - Latest turn - + + + Working tree + + + Branch changes + + + Latest turn + + Turn - {orderedTurnDiffSummaries.map((summary) => { - const turnCount = - summary.checkpointTurnCount ?? - inferredCheckpointTurnCountByTurnId[summary.turnId] ?? - "?"; - return ( - selectTurn(summary.turnId)} - > - Turn {turnCount} - - {formatShortTimestamp(summary.completedAt, settings.timestampFormat)} - - - ); - })} + + {orderedTurnDiffSummaries.map((summary) => { + const turnCount = + summary.checkpointTurnCount ?? + inferredCheckpointTurnCountByTurnId[summary.turnId] ?? + "?"; + return ( + + + Turn {turnCount} + + {formatShortTimestamp(summary.completedAt, settings.timestampFormat)} + + + + ); + })} + diff --git a/apps/web/src/components/files/FileBreadcrumbs.tsx b/apps/web/src/components/files/FileBreadcrumbs.tsx index 4896c9ce2d..6f69aba45e 100644 --- a/apps/web/src/components/files/FileBreadcrumbs.tsx +++ b/apps/web/src/components/files/FileBreadcrumbs.tsx @@ -10,6 +10,8 @@ import { MenuGroup, MenuItem, MenuPopup, + MenuRadioGroup, + MenuRadioItem, MenuSeparator, MenuTrigger, } from "~/components/ui/menu"; @@ -143,36 +145,51 @@ function BreadcrumbMenuContent(props: { : "This folder is empty."} ) : ( - children.map((entry) => { - const isCurrentFile = entry.kind === "file" && entry.path === props.currentFilePath; - return ( - { - if (entry.kind === "directory") { - props.onDirectoryChange(entry.path); - return; - } - props.onOpenChange(false); - props.onOpenFile(entry.path); - }} - > - - - }> - {entry.label} - - - {entry.path} - - - {entry.kind === "directory" ? : null} - - ); - }) + // Files form a radio group keyed by path so the open file is marked as checked; + // directories only navigate the menu, so they stay plain items. + { + props.onOpenChange(false); + props.onOpenFile(path); + }} + > + {children.map((entry) => { + const isCurrentFile = entry.kind === "file" && entry.path === props.currentFilePath; + const row = ( + <> + + + }> + {entry.label} + + + {entry.path} + + + + ); + return entry.kind === "directory" ? ( + props.onDirectoryChange(entry.path)} + > + {row} + + + ) : ( + + {row} + + ); + })} + )} {entriesQuery.error && entriesQuery.data !== null ? ( diff --git a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx index af1d40cd00..fe8052afa8 100644 --- a/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestCodeTab.tsx @@ -69,6 +69,8 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, DropdownMenuTrigger, } from "../ui/menu"; import { toastManager } from "../ui/toast"; @@ -1048,31 +1050,31 @@ function PullRequestCodeTab({ - onSelectedCommitChange(null)} + onSelectedCommitChange(value === "all" ? null : value)} > - All commits - - {orderedCommits.slice(0, visibleCommitCount).map((entry) => ( - onSelectedCommitChange(entry.oid)} - > - {/* Headlines run long, and the abbreviated oid after one is what a reader - matches against the commit list on the host. */} - - {entry.messageHeadline}} - /> - {entry.messageHeadline} - - - {entry.oid.slice(0, 7)} - - - ))} + + All commits + + {orderedCommits.slice(0, visibleCommitCount).map((entry) => ( + + {/* Headlines run long, and the abbreviated oid after one is what a reader + matches against the commit list on the host. */} + + + {entry.messageHeadline}} + /> + {entry.messageHeadline} + + + {entry.oid.slice(0, 7)} + + + + ))} + {orderedCommits.length > visibleCommitCount ? ( // Kept out of the radio group: it changes how much of the list is on screen // rather than what the diff is scoped to. From 0847708431ce09f2c87cab03ab6ecffcf1708bef Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 14:34:25 -0600 Subject: [PATCH 044/104] fix(web): keep provider account emails readable (#810) --- apps/web/src/components/settings/ProviderInstanceCard.tsx | 4 ++-- apps/web/src/components/settings/RedactedSensitiveText.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/settings/ProviderInstanceCard.tsx b/apps/web/src/components/settings/ProviderInstanceCard.tsx index f20412d84e..b14e9032f9 100644 --- a/apps/web/src/components/settings/ProviderInstanceCard.tsx +++ b/apps/web/src/components/settings/ProviderInstanceCard.tsx @@ -189,7 +189,7 @@ function ProviderAuthEmail(props: { if (!trimmed) return null; return ( - + {props.separator ? · : null} {props.prefix ? {props.prefix} : null} ) : null; const statusLineClassName = - "flex min-w-0 flex-wrap items-center gap-x-1.5 text-[13px] leading-[1.45] text-muted-foreground/80"; + "flex min-w-0 flex-wrap items-baseline gap-x-1.5 text-[13px] leading-[1.45] text-muted-foreground/80"; // The editor's status line. It doubles as the account identity row, so the // redacted email lives here rather than in the list, where a masked address diff --git a/apps/web/src/components/settings/RedactedSensitiveText.tsx b/apps/web/src/components/settings/RedactedSensitiveText.tsx index 95b31b4036..cc29228ae4 100644 --- a/apps/web/src/components/settings/RedactedSensitiveText.tsx +++ b/apps/web/src/components/settings/RedactedSensitiveText.tsx @@ -44,7 +44,7 @@ export function RedactedSensitiveText(props: { + ); } @@ -2751,7 +2785,7 @@ function WorkGroupToggleTimelineRow({ return ( ); } @@ -4094,7 +4129,7 @@ const PlainWorkEntryRow = memo(function PlainWorkEntryRow(props: { }) { const { workEntry, workspaceRoot, isExpandedToolGroupEntry, displayLabel } = props; const activity = use(TimelineRowActivityCtx); - const { threadRef, onImageExpand } = use(TimelineRowCtx); + const { threadRef, onImageExpand, timestampFormat } = use(TimelineRowCtx); const groupView = use(WorkGroupViewCtx); const [expanded, setExpanded] = useState( () => groupView?.state.expandedEntries.has(workEntry.id) ?? false, @@ -4206,7 +4241,7 @@ const PlainWorkEntryRow = memo(function PlainWorkEntryRow(props: { return (
) : null} + Date: Thu, 24 Sep 2026 16:07:55 -0600 Subject: [PATCH 069/104] fix(mobile): ignore bare wake links without navigation routes (#772) --- apps/mobile/src/App.tsx | 10 ++------ apps/mobile/src/lib/appLinking.test.ts | 34 ++++++++++++++++++++++++++ apps/mobile/src/lib/appLinking.ts | 10 ++++++++ 3 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 apps/mobile/src/lib/appLinking.test.ts create mode 100644 apps/mobile/src/lib/appLinking.ts diff --git a/apps/mobile/src/App.tsx b/apps/mobile/src/App.tsx index 8095205a55..c33e6ca89c 100644 --- a/apps/mobile/src/App.tsx +++ b/apps/mobile/src/App.tsx @@ -24,6 +24,7 @@ import { OverlayPortalHost } from "./components/OverlayPortal"; import { useUniwindTheme } from "./lib/useUniwindTheme"; import { appBlurTargetRef } from "./lib/appBlurTarget"; import { useMobileNavigationTheme } from "./lib/useMobileNavigationTheme"; +import { shouldHandleAppLink } from "./lib/appLinking"; import "../global.css"; @@ -41,14 +42,7 @@ const appLinking = { // schemes `app.config.ts` registers, because iOS never delivers an // unregistered scheme to the app. prefixes: [Linking.createURL("/"), "pylon-code://", "pylon-code-dev://", "pylon-code-preview://"], - // The Expo dev client launches the app via - // ://expo-development-client/?url= — that URL addresses - // the launcher, not app navigation. Without this filter it falls through - // to the NotFound wildcard route on every dev launch. - // expo-sharing uses a private lifecycle URL only to wake the app. The - // persisted share inbox below owns navigation once the payload is durable. - filter: (url: string) => - !url.includes("expo-development-client") && !url.includes("://expo-sharing"), + filter: shouldHandleAppLink, }; const Navigation = createStaticNavigation(RootStack); diff --git a/apps/mobile/src/lib/appLinking.test.ts b/apps/mobile/src/lib/appLinking.test.ts new file mode 100644 index 0000000000..ad75f3271b --- /dev/null +++ b/apps/mobile/src/lib/appLinking.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { shouldHandleAppLink } from "./appLinking"; + +describe("shouldHandleAppLink", () => { + it.each([ + "pylon-code://", + "pylon-code:///", + "pylon-code-dev://", + "pylon-code-dev:///", + "pylon-code-preview://", + "pylon-code-preview:///", + "PYLON-CODE://", + ])("keeps the current route for wake-only link %s", (url) => { + expect(shouldHandleAppLink(url)).toBe(false); + }); + + it.each([ + "pylon-code://threads/env-1/thread-1", + "pylon-code-dev://threads/env-1/thread-1", + "pylon-code-preview://settings/environments", + "pylon-code://pair?pairingUrl=https%3A%2F%2Fexample.test", + "pylon-code://?pairingUrl=https%3A%2F%2Fexample.test", + ])("preserves navigable and pairing link %s", (url) => { + expect(shouldHandleAppLink(url)).toBe(true); + }); + + it.each([ + "pylon-code-dev://expo-development-client/?url=packager", + "pylon-code://expo-sharing/anything", + ])("ignores native lifecycle link %s", (url) => { + expect(shouldHandleAppLink(url)).toBe(false); + }); +}); diff --git a/apps/mobile/src/lib/appLinking.ts b/apps/mobile/src/lib/appLinking.ts new file mode 100644 index 0000000000..4d48974449 --- /dev/null +++ b/apps/mobile/src/lib/appLinking.ts @@ -0,0 +1,10 @@ +/** Ignore lifecycle and wake-only URLs before they reach React Navigation. */ +export function shouldHandleAppLink(url: string): boolean { + if (url.includes("expo-development-client") || url.includes("://expo-sharing")) { + return false; + } + + // iOS dictation keyboards can return to the app with only its scheme. Those + // URLs wake the app but have no route, so navigation must keep its stack. + return !/^pylon-code(?:-dev|-preview)?:\/*$/i.test(url); +} From 21247b7903607a4a7e3d65a7bfe8a466a8035193 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 16:08:59 -0600 Subject: [PATCH 070/104] fix(web): show pull request edit actions on touch (#802) --- .../pullRequest/PullRequestDetailPanel.tsx | 11 +++----- .../pullRequest/PullRequestEditButton.tsx | 27 +++++++++++++++++++ .../PullRequestReviewAnnotation.tsx | 11 +++----- .../pullRequest/PullRequestSummaryTab.tsx | 21 +++------------ .../pullRequest/PullRequestTimelineTab.tsx | 12 +++------ 5 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 apps/web/src/components/pullRequest/PullRequestEditButton.tsx diff --git a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx index 665f085d27..3eaea42e8f 100644 --- a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx +++ b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx @@ -36,7 +36,6 @@ import { LinkIcon, MoreHorizontalIcon, PanelRightIcon, - PencilIcon, PlayIcon, RotateCcwIcon, TriangleAlertIcon, @@ -112,6 +111,7 @@ import { import { EnvironmentMachineIcon } from "../EnvironmentMachineIcon"; import { Badge } from "../ui/badge"; import { Button } from "../ui/button"; +import { PullRequestEditButton } from "./PullRequestEditButton"; import { Input } from "../ui/input"; import { Toggle, ToggleGroup } from "../ui/toggle-group"; import { @@ -2414,15 +2414,10 @@ export function PullRequestDetailPanel({ {detail.title} {canEditPullRequestChangeRequest(detail) ? ( - + /> ) : null}
) : ( diff --git a/apps/web/src/components/pullRequest/PullRequestEditButton.tsx b/apps/web/src/components/pullRequest/PullRequestEditButton.tsx new file mode 100644 index 0000000000..b04ec17b87 --- /dev/null +++ b/apps/web/src/components/pullRequest/PullRequestEditButton.tsx @@ -0,0 +1,27 @@ +import { PencilIcon } from "lucide-react"; +import type { ComponentProps } from "react"; + +import { cn } from "../../lib/utils"; +import { Button } from "../ui/button"; + +/** Edit affordances stay visible on touch devices and reveal on hover or focus. */ +export function PullRequestEditButton({ + className, + ...props +}: Omit, "children" | "render" | "size" | "variant"> & { + "aria-label": string; +}) { + return ( + + ); +} diff --git a/apps/web/src/components/pullRequest/PullRequestReviewAnnotation.tsx b/apps/web/src/components/pullRequest/PullRequestReviewAnnotation.tsx index f1e8955c3d..d49cb52cc3 100644 --- a/apps/web/src/components/pullRequest/PullRequestReviewAnnotation.tsx +++ b/apps/web/src/components/pullRequest/PullRequestReviewAnnotation.tsx @@ -14,7 +14,6 @@ import { CircleIcon, HammerIcon, MessageSquareIcon, - PencilIcon, Trash2Icon, } from "lucide-react"; import { useRef, useState } from "react"; @@ -23,6 +22,7 @@ import { formatRelativeTimeLabel } from "~/timestampFormat"; import { cn } from "~/lib/utils"; import { Button } from "../ui/button"; +import { PullRequestEditButton } from "./PullRequestEditButton"; import { Textarea } from "../ui/textarea"; import { isCommentSubmitShortcut } from "../diffs/commentSubmitShortcut"; import { @@ -297,15 +297,10 @@ export function ReviewThreadCard({ environmentId={environmentId} /> {canEditComment(comment) ? ( - + /> ) : null} )} diff --git a/apps/web/src/components/pullRequest/PullRequestSummaryTab.tsx b/apps/web/src/components/pullRequest/PullRequestSummaryTab.tsx index be1dffb295..0937c1ee3e 100644 --- a/apps/web/src/components/pullRequest/PullRequestSummaryTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestSummaryTab.tsx @@ -12,7 +12,6 @@ import { ChevronDownIcon, ChevronRightIcon, HammerIcon, - PencilIcon, TagIcon, UsersIcon, } from "lucide-react"; @@ -25,6 +24,7 @@ import { useOpenLink } from "~/browser/useOpenLink"; import { formatRelativeTimeLabel } from "~/timestampFormat"; import { Button } from "../ui/button"; +import { PullRequestEditButton } from "./PullRequestEditButton"; import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; import { toastManager } from "../ui/toast"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; @@ -194,15 +194,7 @@ function CommentBody({ threadRef={editing.threadRef} /> {editing.canEdit(comment) ? ( - + editing.onEdit(comment)} /> ) : null} ); @@ -871,15 +863,10 @@ export function PullRequestSummaryTab({ threadRef={threadRef} /> {canEditPullRequestChangeRequest(detail) ? ( - + /> ) : null} )} diff --git a/apps/web/src/components/pullRequest/PullRequestTimelineTab.tsx b/apps/web/src/components/pullRequest/PullRequestTimelineTab.tsx index b114250549..5d0ba801ed 100644 --- a/apps/web/src/components/pullRequest/PullRequestTimelineTab.tsx +++ b/apps/web/src/components/pullRequest/PullRequestTimelineTab.tsx @@ -12,7 +12,6 @@ import { FileCode2Icon, GitCommitHorizontalIcon, MessageSquareIcon, - PencilIcon, } from "lucide-react"; import { useState, type ReactNode } from "react"; @@ -23,6 +22,7 @@ import { useAtomCommand } from "~/state/use-atom-command"; import { formatRelativeTimeLabel } from "~/timestampFormat"; import { Button } from "../ui/button"; +import { PullRequestEditButton } from "./PullRequestEditButton"; import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from "../ui/collapsible"; import { toastManager } from "../ui/toast"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; @@ -235,15 +235,11 @@ function ConversationCard({ {editable !== null && !editing ? ( - + /> ) : null} {reactions.canReact || event.reactions.length > 0 ? ( Date: Thu, 24 Sep 2026 16:09:25 -0600 Subject: [PATCH 071/104] docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- .agents/upstream-review.md | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.agents/upstream-review.md b/.agents/upstream-review.md index 0a3b36ea40..6a49f4c5ad 100644 --- a/.agents/upstream-review.md +++ b/.agents/upstream-review.md @@ -15,7 +15,7 @@ The maintainer authorized compatible catch-up and routine adaptations that prese ## Active cycle -None currently active. Upstream integration cycle #689 is completed. +[Upstream catch-up cycle #736](https://github.com/pylon-code/pylon/issues/736) is active. Its frozen review range is `bbedad0278bbf753503184c00e0c09a0eab6679c..e67abcf798f8c4d8458755e3b4dde02c2c1f628b` (481 sources). The issue holds the source-by-source disposition and actionable work queue, including open PRs, deliberate Pylon divergences, dependencies, and product decisions. This review does not advance the completed integration cursor `fa1e1715538f6f8ac076761f7821f68479f616ab` or claim the open implementations are adopted. The Oh My Pi integration was explicitly rejected and is excluded from this cycle. Cycle #689 remains the latest completed integration. ## Latest cycle @@ -33,26 +33,26 @@ New records use the compact group format in [the decision framework](skills/revi Historical groups are indexed in the linked archive. This file migration changes no source disposition, review cursor, deferred trigger, watch state, product code, or Git ancestry. -| Group / bounded head | Sources | Outcome and remaining scope | Pylon PR / verification | -| ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Explicit provider refresh cache bypass / `f193a6863c494fdfa36a1ef3132e42a7d7b42926` | `f193a6863c494fdfa36a1ef3132e42a7d7b42926` (#13109) | Implemented in PR #743, pending merge: explicit refresh bypasses the model manifest freshness/retry timer and provider-owned capability, model and package-version caches before status probes. Background probes retain their timers; Pylon's installed-provider `refreshModels` path remains. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #743](https://github.com/pylon-code/pylon/pull/743); independent adversarial review, 246 focused tests, server typecheck and scoped lint/format. | -| Background GitHub PR quota / `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` | `5975ec78b7eaff654e6183b99f8de7a05591e99d` (#13189), `18de6bb328059703b5d1b0661610076e3c0fa2d4` (#13198), `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` (#13200) | Adapted all three: skip uncached recheck when a terminal link cannot settle, batch background PR summaries, and avoid owner-qualified head probes. Preserve Pylon credential scopes, GraphQL budget and per-environment routing; exclude upstream-only test fixture APIs. No cursor advance. | [GitHub quota #749](https://github.com/pylon-code/pylon/pull/749); focused server regressions, scoped lint and typecheck. | -| OTLP export kill switch / `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` | `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` (#13355) | Implemented in PR #746, pending merge: `OTEL_SDK_DISABLED` and the inherited `T3CODE_OTEL_SDK_DISABLED` override suppress server and desktop main-process OTLP traces, metrics, and logs from environment, bootstrap, or Settings endpoints; WSL inherits the flags. Local trace files and local logs remain active, and Pylon service identity is retained. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #746](https://github.com/pylon-code/pylon/pull/746); independent adversarial review, 80 focused tests, server/desktop/shared typechecks and scoped lint/format. | -| OTLP log export, desktop main process telemetry, and per-signal observability / `fa1e1715538f6f8ac076761f7821f68479f616ab` | `3bb06ad910389fcfc886bd5156f41dc73f307f80` (#12493), `82059df1523d070b00ab6f45159115f626a4977e` (#12520), `a8693eb4ed3a66d72e6b814117e7beeaf0fb5f8b` (#12540), `fa1e1715538f6f8ac076761f7821f68479f616ab` (#12657) | Adopted all four in Cycle #689 (PR #690). Server exports structured log records over OTLP with trace/span ID correlation, replaces `tracerLogger` to eliminate duplicate in-span span-event exports, and applies dedicated flusher/serialization layers. Desktop exports main process traces and logs over OTLP while withholding metric emissions until metrics are instrumented. OTLP settings and HTTP header sets are isolated per signal (traces, metrics, logs) across server and desktop configurations. Adversarial review findings resolved: preserved `pylon-server` runtime and service identity defaults, verified `DEFAULT_SIGNAL_EXPORT` fallback, tested backward compatibility of `ServerObservability` schema decoding, and validated log deduplication. Cursor unchanged. | Cycle [#689](https://github.com/pylon-code/pylon/issues/689), [PR #690](https://github.com/pylon-code/pylon/pull/690); independent adversarial review, new ServerLogger test suite, 9 desktop observability tests, all CI checks green. | -| Web chat/diff polish, editor discovery, diff cache eviction, ACP elicitation, and preview recovery / `c14f6015bfe479d313355cb234af1a5c16dbb15f` | `c14f6015bfe479d313355cb234af1a5c16dbb15f` (#12453), `0ff87f251dafb32703d5f531e7b248ad0a581ee2` (#10831), `4a560b4e4ebb37efb7f57805ba79e37f5500bdca` (#12438), `9accc567670da5096e9a694c1adb0d35adb51d9d` (#12571), `dfbb11bdd7c3f1a5575cb55d3e3abb12be025727` (#12377), `408ff8ae9bd7eb2e7e90cbfd8b3fcfe63641bf23` (#12439), `cb3d95c17487f3d834a5537f7c3aa0106bc9b5ec` (#12523), `de6a230db0bb71d3fa91d8de0ae7067b68253f82` (#11294), `efb96939fbb135b9f4de3bff19447200b178b23a` (#7861), `5378f87f99175bded2b9241876319567875aca03` (#12535) | Adopted all ten across Cycle #685 Group A (PR #686) and Group B (PR #687). Retained whitespace when copying diff contents, kept linebreaks in citation content, opened chat popovers with relative placement, discovered installed external editors on desktop outside PATH, wrapped long titles in confirmation dialogs, showed plain text in collapsed thought previews, released oversized pull request diff cache entries, accepted ACP SDK elicitation requests (`session/elicitation` & `elicitation/create`), surfaced Codex app permission requests as approvable, and recovered preview host registration after request timeouts via connection queue eviction. Hardening and adversarial review findings resolved: normalized `acceptAlways` approval decision in Codex session runtime to grant permissions, enforced retirement epoch and quarantine boundaries on approval requests, scoped event emission with `CodexNotificationEpoch`, wrapped diff cache invalidation in `Effect.uninterruptible`, cleaned up mobile icon maps, and rebranded pre-existing PR tool descriptions to Pylon. Preserved Pylon Effect services, session lifecycles, and UI layout authority. Cursor unchanged. | Cycle [#685](https://github.com/pylon-code/pylon/issues/685), [PR #686](https://github.com/pylon-code/pylon/pull/686) and [PR #687](https://github.com/pylon-code/pylon/pull/687); independent adversarial reviews, 260+ focused unit tests across web, server, and effect-acp, full workspace typecheck, scoped format/lint. | -| Web interaction, ACP session startup stderr, Claude continuation, and GTK4 snapshot / `52d08a14b9e475371658fabbb12c056c3d3dad67` | `8dd02470b1e7603b8d36a21ae27c510480351f18` (#12552), `7445aa733ada33e45289e5aa5055f79142556513` (#11263), `599c9776eb887d4fff42da7d2daf8aa07ee9a9e2` (#11268), `dcf8942304cfef382af55c0068cb2f04c86757fc` (#12636), `e36725682bd2935f1bcfdf81660cb569b5f3df29` (#12577), `63ff33756c47f127391962458d8388a647e8ed44` (#12624), `d6f291303ddc0c9a14f570266a4d9eff6d431593` (#12625), `52d08a14b9e475371658fabbb12c056c3d3dad67` (#12635) | Adopted all eight across Cycle #682 Group A (PR #683) and Group B (PR #684). Preserved draft question input when clicking options, desktop CSP screenshot annotations, restored providers settings heading, aligned PR detail menu glyphs. Empty Claude homePath correctly resolves and shares continuation with `~/.claude` or `CLAUDE_CONFIG_DIR`, surfaced ACP stderr excerpt in session startup failures instead of generic closed session errors, and extracted desktop accessible text for Flatpak and GTK4 apps in SnapShot. Adversarial review findings resolved: ACP stderr excerpt sanitized against tokens/credentials and pending buffer bounded; defensive empty `homePath` trimming and `CLAUDE_CONFIG_DIR` resolution in OAuth usage; desktop accessibility timeout preserved without premature concurrency overlap. Preserved Pylon provider rate limits and error reporting. Cursor unchanged. | Cycle [#682](https://github.com/pylon-code/pylon/issues/682), [PR #683](https://github.com/pylon-code/pylon/pull/683) and [PR #684](https://github.com/pylon-code/pylon/pull/684); independent adversarial reviews, 225+ focused tests across desktop and server, full workspace typecheck, scoped format/lint. | -| Chat message heading accessibility and compact sidebar density / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `6cdbf76fa4b0509913046abe6105037ded9e52bb` (#11199), `ca2cc1339bbd1904b932c3a92d23a2c0ffe1d267` (#11525), `77bca8b2d76a1f42552e5eee7d277fcb1160347a` (#9417), `df7ccc8fd01f5d2a1d8ec21bef8f9c59398fe913` (#11644), `7b61099886f3aac139ad43fb73873c73ba7baf4f` (#11652), `0118b52295264a73ef7cd045437181f7b3e5a99f` (#11595) | Adopted all six across Cycle #669 Group A and Group B. Screen readers navigate chat messages as headings with hierarchical levels (`h3` at offset 3); proposed plans expose semantic headings. Compact sidebar rail (`compactSidebarEnabled`) and compact thread row density (`sidebarCompactThreadRows`) added as opt-in settings defaulting to `false`, preserving Pylon's pull request badges and stack integration, custom settings, and avoiding dead delegation UI. Sparse shelves stay pinned to bottom. Adversarial review findings resolved: accessible fallback for `plan-ready` dot in compact rows, drag layout projection fix for compact rail mode, explicit test fixtures. Cursor unchanged. | Cycle [#669](https://github.com/pylon-code/pylon/issues/669), [PR #679](https://github.com/pylon-code/pylon/pull/679) and [PR #680](https://github.com/pylon-code/pylon/pull/680); independent adversarial reviews, 490+ focused tests, `@t3tools/web`, `@t3tools/desktop`, and `@t3tools/contracts` typechecks, scoped lint/format, green CI. | -| Navigation, search and diff safety / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `9ea9c3d5d2c444133e3ddff40eecf38737951589` (#11075), `886c83450797c46ee84ae28fc16c2208cafa965e` (#10909), `c557bb10aa7cbb78a50df230d1e8e24af79cb662` (#11761), `3fd5d6439d8fd49d173503ecda96500463a39bd2` (#12315) | Adopted all four in Cycle #667 Group B. Symlink file type changes no longer crash diff view; chat folder links reveal directory in file tree; sidebar search matches thread message contents; numbered jump shortcuts do not steal browser tab navigation. Preserved Pylon's keybinding identity, routing and panel-choice semantics. Cursor unchanged. | Cycle [#667](https://github.com/pylon-code/pylon/issues/667), [PR #671](https://github.com/pylon-code/pylon/pull/671); 389 focused tests, web/shared typechecks, scoped lint, green CI. | -| Composer banner density and scroll-pill stability / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `4749035bda13b4b6260499caedbc0d69a2f60e6f` (#12166), `fcfd9f911a27ee3b813d64582c53d82dfa2e892b` (#12317) | Adopted both. Banner stack items gain an optional `compact` flag: compact rows keep the narrow layout and the description that collapses into an info popover below 400px, while other banners get room for the description beside the title, with a labelled, bounded, scrollable popover. The scroll-to-end clearance is split out of the overlay-height publisher and recomputed from a stored height when the pill mounts, so a fling during the composer's resting tween no longer publishes a stale height and yanks the scroll position. **Excluded:** upstream's `approval` banner layout classes — Pylon's `ComposerBanner.Row` union is `inline | wrap-actions | wrap-actions-narrow`and the approval layout arrives in an unadopted commit; only this source's`justify-end`change was applied. Pylon adaptations:`ComposerTasksBadge`keeps its delegates label, expanded guard and`TaskProgressSegments fit`, with the delegates label moved to the same container query as the segment bar so the pair no longer flips at different widths; `ChatView`'s pull-request resolution block is preserved; and five Pylon banners with short static descriptions (environment update, thread woke, snoozed/settled, resume-compaction, auto-balance) are marked `compact` so they do not grow a permanently redundant details popover. Cursor unchanged. | [Chat rendering #668](https://github.com/pylon-code/pylon/pull/668); independent adversarial review, 184 focused tests including new `compact` coverage, `@t3tools/web` typecheck, scoped lint. Two upstream-inherited defects recorded in the PR rather than fixed here. | -| Checkpoint capture reliability and shared-workspace rewind / `93e04160a0c0dece8a258384d2118a660415a53d` | `869347bc26051bba7c3c0274a7dfcae7770d3e3a` (#11665), `1455cb5c35e50211145073be6ca155d56684f0f4` (#12181), `67993623afb14c017bb0b959478dc892f9c427da` (#12307), `b4620d595554654c259ec7c0afb3d3d5532959a8` (#12306), `d17f46d7631b178b6834596264f0f44f210d584b` (#12308) | Adopted all five. Transient git exits retry during capture behind a new optional `retryable` hint on `VcsProcessExitError`; capture recovers nested repositories without commits under index-lock recovery and bounded probe timeouts; a failed baseline lookup logs and treats the ref as absent instead of losing the checkpoint; the `@`-mention entry refresh moves to a coalescing drainable worker chained into `CheckpointReactor.drain`. File rewind is refused unless the thread's cwd is its own worktree with no other thread or live provider session nested inside or above it, and web hides the destructive control for shared directories. **Capability removal, maintainer-approved 2026-09-18**: threads running in the project directory rewind the conversation only, with no override path. Pylon adaptation: the isolation check is scoped with `restoreFiles !== false` so conversation-only rewind still reaches Pylon's existing provider-capability explanation rather than the isolation message or silence, since Pylon's rollback saga services are optional. Pylon git identity, rollback revert action and wire compatibility preserved. No excluded source behavior; cursor unchanged. | [Checkpoint reliability #665](https://github.com/pylon-code/pylon/pull/665); 122 focused tests including three new `restoreFiles` regressions where none existed, `t3`/`contracts`/`web` typechecks, scoped lint (0 errors), rebased onto `origin/pylon` after #653. | -| Provider event log bounds and Codex image attachments / `93e04160a0c0dece8a258384d2118a660415a53d` | `b17cc2ab5d5029f898121798ca19db3288cba616` (#12305, partial), `3fd21df62da3191f1abf12a0cd74df6de9ed30a2` (#11050) | `3fd21df62d` adopted: Codex image attachments pass as `{ type: "localImage", path }` instead of base64 data URLs, so `turn/start` no longer scales with file size. `localImage` is a real content type in the generated Codex app-server schema; Pylon's attachment-id validation against `attachmentsDir` and its `FileSystem` wiring are retained. `b17cc2ab5d` **partially adopted**: record traversal is bounded before serialization with character, field and depth budgets, cycle detection, a `summarizeProviderEvent` fallback that keeps routing and failure fields, and a post-serialization byte-length fallback; new transient suppressions for `turn/diff/updated` and `stream_event`/`content_block_delta`. Pylon's `commitGuard` and existing transient filters preserved. **Excluded: the `stage === "raw"` suppression and `stage === "decoded"` unwrap** (see EXC-1). Cursor unchanged. | [Provider payload bounds #653](https://github.com/pylon-code/pylon/pull/653), merged `4be539f4d48af6ffbe545e015ec246fbb271c92e`; 133 focused tests, `t3` typecheck, scoped format/lint, all final-head CI jobs green, no bot findings. | -| PR comments, controls and avatars / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `f0a0ead946ca3f3f310460a91b7dabb0e3fe4834` (#11962), `f45c6b4a755905485431872f4a4188de3ed05974` (#11994), `c12ba7581ac00ba2a623ad23283da5d03d34c2d8` (#12150), `962bf62921c0ecbc9320f9b071e16b1d41484aad` (#12125), `df466c79810046d20c0f3ccc4e51709c5703814e` (#11728) | Adopted all five. Narrow controls, keyboard submission, paged bot/finished groups, long-comment previews, detail avatars and image-failure fallback. Pylon corrections align bot app links, lazily page finished groups, and scroll after collapsing. Optional actor metadata preserves wire compatibility; existing bounded GitHub enrichment/fallback limits remain. No excluded source behavior; cursor unchanged. | [PR review #621](https://github.com/pylon-code/pylon/pull/621); focused regressions, scoped types/lint, independent Opus adversarial reviews, browser before/after evidence and comment interaction recording. | -| Large diffs and review panel defaults / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `aae361c4ef6478e4dcf970d574f3fd38d908e6b9` (#10822), `36caf200c2b24c12838570966f5c85786a1fc1f8` (#11484), `3efdcc5296f1754e0f3bf7fee5fc2ada510e0438` (#11931), `6d1d549441be84f19696ab59ed7e2fbf305280d4` (#12190), `52ad70ec43b5dcfcdc72ac79c0964da8e4d163db` (#12142), `394af73e02238e551db14f44d57f1469dd2942aa` (#12139) | Adopted all six. Complete Git metadata and progressive per-file patches on web/mobile; preserve old-server preview fallback, literal paths and bounded output. Tree order/folder state survive refresh. Files default collapsed while explicit saved choices persist. Generic diff openings reset to working tree; linked PRs outrank automatic diffs. Preserve Pylon multi-PR authority, environment identity, responsive layout and existing settings. No excluded source behavior; cursor unchanged. | [Review diffs #616](https://github.com/pylon-code/pylon/pull/616); 509 integrated focused tests plus 79 unchanged backend regressions, six scoped package typechecks, lint, independent Antigravity adversarial reviews, before/after browser evidence and refresh video. Native verification details in PR. | -| Usage pools, hub quotas, reset credits and local composer action / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 16-source list in the usage pilot PR; parent `19d8ab2ae9fc562ee7b216a0d72903fbfafa9572`, final hub-routing fix `6abdf37a50ce6c1c9fabc499f4d0e159a6182d90` | Pylon port of the final behavior; parent partially adopted with native Claude scoped push mapping retained in DEF-16. `00d6109cbb1a13712d7347701969870aee83252d` is covered: demo fixtures were never introduced. Native Claude quota reads reuse Pylon's cached OAuth parser; no duplicate SDK probe or ingestion worker. | [Usage pilot #400](https://github.com/pylon-code/pylon/pull/400); focused quota, retry, RPC and compatibility checks, web/mobile evidence in PR. | -| Pull-request browsing, cached reads and opt-in panel behavior / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 16-source disposition list in #401; 15 adopted, #9877 deferred | Final PR state icons, authored/readiness sorting, seeded detail, keyboard pickers, link previews, profile links, revision-aware caches, project-filter deduplication and manual panel priority. Preserve Pylon server-projected PR tracking and quota reservation/refund semantics. Copy priority remains DEF-17. | [PR workflow #401](https://github.com/pylon-code/pylon/pull/401); 768 focused tests, affected package types, scoped lint and integrated web evidence. | -| Composer interaction, layout and notice follow-ups / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 27-source disposition list in #402 | Final scroll-only resting behavior, readable multiline drafts, stable control measurements, last-message reservation, narrow-width controls and opt-in context meter. Preserve Pylon provider locks, quick questions, session/harness/goal actions, quota placement, upload handling and diagnostic errors. Temporary blur-selection holding in #9499 is superseded by #10437; no unresolved scope in this group. | [Composer #402](https://github.com/pylon-code/pylon/pull/402); 724 focused tests, six affected package typechecks, scoped lint, integrated web screenshots and motion evidence. | -| Changed-file trees and stable diff navigation / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full eight-source list in #403; seven new sources and DEF-11 | Adopted: persistent folder trees, diff/PR trees, repeated selection, saved layout, wrapped edit heights, search focus and folder sorting. Completes DEF-11 after removing all helper callers. Pylon lazy-viewer scope guards remain; narrow panels place the tree below code. | [File navigation #403](https://github.com/pylon-code/pylon/pull/403); 271 focused tests, six package typechecks, scoped lint/export checks and integrated web evidence. | -| Thread link targets / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Three full source SHAs in [#404](https://github.com/pylon-code/pylon/pull/404). | Adopted link target preference across chat, terminal and PR surfaces; completed DEF-12 and DEF-15 after replacing their last callers. Preserve Pylon context/routing, browser defaults, safe errors and external fallback. Browser profiles remain an independent source group. | [#404](https://github.com/pylon-code/pylon/pull/404); 233 focused tests, seven package typechecks, targeted lint/export checks; web evidence in PR. | +| Group / bounded head | Sources | Outcome and remaining scope | Pylon PR / verification | +| ----------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Explicit provider refresh cache bypass / `f193a6863c494fdfa36a1ef3132e42a7d7b42926` | `f193a6863c494fdfa36a1ef3132e42a7d7b42926` (#13109) | Implemented in PR #743, pending merge: explicit refresh bypasses the model manifest freshness/retry timer and provider-owned capability, model and package-version caches before status probes. Background probes retain their timers; Pylon's installed-provider `refreshModels` path remains. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #743](https://github.com/pylon-code/pylon/pull/743); independent adversarial review, 246 focused tests, server typecheck and scoped lint/format. | +| Background GitHub PR quota / `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` | `5975ec78b7eaff654e6183b99f8de7a05591e99d` (#13189), `18de6bb328059703b5d1b0661610076e3c0fa2d4` (#13198), `eafb4a9340bd2c2271b7c8fac7eddd55beea3972` (#13200) | Adapted all three: skip uncached recheck when a terminal link cannot settle, batch background PR summaries, and avoid owner-qualified head probes. Preserve Pylon credential scopes, GraphQL budget and per-environment routing; exclude upstream-only test fixture APIs. No cursor advance. | [GitHub quota #749](https://github.com/pylon-code/pylon/pull/749); focused server regressions, scoped lint and typecheck. | +| OTLP export kill switch / `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` | `e67abcf798f8c4d8458755e3b4dde02c2c1f628b` (#13355) | Implemented in PR #746, pending merge: `OTEL_SDK_DISABLED` and the inherited `T3CODE_OTEL_SDK_DISABLED` override suppress server and desktop main-process OTLP traces, metrics, and logs from environment, bootstrap, or Settings endpoints; WSL inherits the flags. Local trace files and local logs remain active, and Pylon service identity is retained. No behavior excluded. Cursor unchanged. | Cycle [#736](https://github.com/pylon-code/pylon/issues/736), [PR #746](https://github.com/pylon-code/pylon/pull/746); independent adversarial review, 80 focused tests, server/desktop/shared typechecks and scoped lint/format. | +| OTLP log export, desktop main process telemetry, and per-signal observability / `fa1e1715538f6f8ac076761f7821f68479f616ab` | `3bb06ad910389fcfc886bd5156f41dc73f307f80` (#12493), `82059df1523d070b00ab6f45159115f626a4977e` (#12520), `a8693eb4ed3a66d72e6b814117e7beeaf0fb5f8b` (#12540), `fa1e1715538f6f8ac076761f7821f68479f616ab` (#12657) | Adopted all four in Cycle #689 (PR #690). Server exports structured log records over OTLP with trace/span ID correlation, replaces `tracerLogger` to eliminate duplicate in-span span-event exports, and applies dedicated flusher/serialization layers. Desktop exports main process traces and logs over OTLP while withholding metric emissions until metrics are instrumented. OTLP settings and HTTP header sets are isolated per signal (traces, metrics, logs) across server and desktop configurations. Adversarial review findings resolved: preserved `pylon-server` runtime and service identity defaults, verified `DEFAULT_SIGNAL_EXPORT` fallback, tested backward compatibility of `ServerObservability` schema decoding, and validated log deduplication. Cursor unchanged. | Cycle [#689](https://github.com/pylon-code/pylon/issues/689), [PR #690](https://github.com/pylon-code/pylon/pull/690); independent adversarial review, new ServerLogger test suite, 9 desktop observability tests, all CI checks green. | +| Web chat/diff polish, editor discovery, diff cache eviction, ACP elicitation, and preview recovery / `c14f6015bfe479d313355cb234af1a5c16dbb15f` | `c14f6015bfe479d313355cb234af1a5c16dbb15f` (#12453), `0ff87f251dafb32703d5f531e7b248ad0a581ee2` (#10831), `4a560b4e4ebb37efb7f57805ba79e37f5500bdca` (#12438), `9accc567670da5096e9a694c1adb0d35adb51d9d` (#12571), `408ff8ae9bd7eb2e7e90cbfd8b3fcfe63641bf23` (#12439), `cb3d95c17487f3d834a5537f7c3aa0106bc9b5ec` (#12523), `de6a230db0bb71d3fa91d8de0ae7067b68253f82` (#11294), `efb96939fbb135b9f4de3bff19447200b178b23a` (#7861), `5378f87f99175bded2b9241876319567875aca03` (#12535) | Adopted nine across Cycle #685 Group A (PR #686) and Group B (PR #687). Retained whitespace when copying diff contents, kept linebreaks in citation content, opened chat popovers with relative placement, discovered installed external editors on desktop outside PATH, wrapped long titles in confirmation dialogs, released oversized pull request diff cache entries, accepted ACP SDK elicitation requests (`session/elicitation` & `elicitation/create`), surfaced Codex app permission requests as approvable, and recovered preview host registration after request timeouts via connection queue eviction. Hardening and adversarial review findings resolved: normalized `acceptAlways` approval decision in Codex session runtime to grant permissions, enforced retirement epoch and quarantine boundaries on approval requests, scoped event emission with `CodexNotificationEpoch`, wrapped diff cache invalidation in `Effect.uninterruptible`, cleaned up mobile icon maps, and rebranded pre-existing PR tool descriptions to Pylon. Preserved Pylon Effect services, session lifecycles, and UI layout authority. Cursor unchanged. | Cycle [#685](https://github.com/pylon-code/pylon/issues/685), [PR #686](https://github.com/pylon-code/pylon/pull/686) and [PR #687](https://github.com/pylon-code/pylon/pull/687); independent adversarial reviews, 260+ focused unit tests across web, server, and effect-acp, full workspace typecheck, scoped format/lint. | +| Web interaction, ACP session startup stderr, Claude continuation, and GTK4 snapshot / `52d08a14b9e475371658fabbb12c056c3d3dad67` | `8dd02470b1e7603b8d36a21ae27c510480351f18` (#12552), `7445aa733ada33e45289e5aa5055f79142556513` (#11263), `599c9776eb887d4fff42da7d2daf8aa07ee9a9e2` (#11268), `dcf8942304cfef382af55c0068cb2f04c86757fc` (#12636), `e36725682bd2935f1bcfdf81660cb569b5f3df29` (#12577), `63ff33756c47f127391962458d8388a647e8ed44` (#12624), `d6f291303ddc0c9a14f570266a4d9eff6d431593` (#12625), `52d08a14b9e475371658fabbb12c056c3d3dad67` (#12635) | Adopted all eight across Cycle #682 Group A (PR #683) and Group B (PR #684). Preserved draft question input when clicking options, desktop CSP screenshot annotations, restored providers settings heading, aligned PR detail menu glyphs. Empty Claude homePath correctly resolves and shares continuation with `~/.claude` or `CLAUDE_CONFIG_DIR`, surfaced ACP stderr excerpt in session startup failures instead of generic closed session errors, and extracted desktop accessible text for Flatpak and GTK4 apps in SnapShot. Adversarial review findings resolved: ACP stderr excerpt sanitized against tokens/credentials and pending buffer bounded; defensive empty `homePath` trimming and `CLAUDE_CONFIG_DIR` resolution in OAuth usage; desktop accessibility timeout preserved without premature concurrency overlap. Preserved Pylon provider rate limits and error reporting. Cursor unchanged. | Cycle [#682](https://github.com/pylon-code/pylon/issues/682), [PR #683](https://github.com/pylon-code/pylon/pull/683) and [PR #684](https://github.com/pylon-code/pylon/pull/684); independent adversarial reviews, 225+ focused tests across desktop and server, full workspace typecheck, scoped format/lint. | +| Chat message heading accessibility and compact sidebar density / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `6cdbf76fa4b0509913046abe6105037ded9e52bb` (#11199), `ca2cc1339bbd1904b932c3a92d23a2c0ffe1d267` (#11525), `77bca8b2d76a1f42552e5eee7d277fcb1160347a` (#9417), `df7ccc8fd01f5d2a1d8ec21bef8f9c59398fe913` (#11644), `7b61099886f3aac139ad43fb73873c73ba7baf4f` (#11652), `0118b52295264a73ef7cd045437181f7b3e5a99f` (#11595) | Adopted all six across Cycle #669 Group A and Group B. Screen readers navigate chat messages as headings with hierarchical levels (`h3` at offset 3); proposed plans expose semantic headings. Compact sidebar rail (`compactSidebarEnabled`) and compact thread row density (`sidebarCompactThreadRows`) added as opt-in settings defaulting to `false`, preserving Pylon's pull request badges and stack integration, custom settings, and avoiding dead delegation UI. Sparse shelves stay pinned to bottom. Adversarial review findings resolved: accessible fallback for `plan-ready` dot in compact rows, drag layout projection fix for compact rail mode, explicit test fixtures. Cursor unchanged. | Cycle [#669](https://github.com/pylon-code/pylon/issues/669), [PR #679](https://github.com/pylon-code/pylon/pull/679) and [PR #680](https://github.com/pylon-code/pylon/pull/680); independent adversarial reviews, 490+ focused tests, `@t3tools/web`, `@t3tools/desktop`, and `@t3tools/contracts` typechecks, scoped lint/format, green CI. | +| Navigation, search and diff safety / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `9ea9c3d5d2c444133e3ddff40eecf38737951589` (#11075), `886c83450797c46ee84ae28fc16c2208cafa965e` (#10909), `c557bb10aa7cbb78a50df230d1e8e24af79cb662` (#11761), `3fd5d6439d8fd49d173503ecda96500463a39bd2` (#12315) | Adopted all four in Cycle #667 Group B. Symlink file type changes no longer crash diff view; chat folder links reveal directory in file tree; sidebar search matches thread message contents; numbered jump shortcuts do not steal browser tab navigation. Preserved Pylon's keybinding identity, routing and panel-choice semantics. Cursor unchanged. | Cycle [#667](https://github.com/pylon-code/pylon/issues/667), [PR #671](https://github.com/pylon-code/pylon/pull/671); 389 focused tests, web/shared typechecks, scoped lint, green CI. | +| Composer banner density and scroll-pill stability / `b44c1ce5d25ee0d5a5be82e380618a886c19ea96` | `4749035bda13b4b6260499caedbc0d69a2f60e6f` (#12166), `fcfd9f911a27ee3b813d64582c53d82dfa2e892b` (#12317) | Adopted both. Banner stack items gain an optional `compact` flag: compact rows keep the narrow layout and the description that collapses into an info popover below 400px, while other banners get room for the description beside the title, with a labelled, bounded, scrollable popover. The scroll-to-end clearance is split out of the overlay-height publisher and recomputed from a stored height when the pill mounts, so a fling during the composer's resting tween no longer publishes a stale height and yanks the scroll position. **Excluded:** upstream's `approval` banner layout classes — Pylon's `ComposerBanner.Row` union is `inline | wrap-actions | wrap-actions-narrow`and the approval layout arrives in an unadopted commit; only this source's`justify-end`change was applied. Pylon adaptations:`ComposerTasksBadge`keeps its delegates label, expanded guard and`TaskProgressSegments fit`, with the delegates label moved to the same container query as the segment bar so the pair no longer flips at different widths; `ChatView`'s pull-request resolution block is preserved; and five Pylon banners with short static descriptions (environment update, thread woke, snoozed/settled, resume-compaction, auto-balance) are marked `compact` so they do not grow a permanently redundant details popover. Cursor unchanged. | [Chat rendering #668](https://github.com/pylon-code/pylon/pull/668); independent adversarial review, 184 focused tests including new `compact` coverage, `@t3tools/web` typecheck, scoped lint. Two upstream-inherited defects recorded in the PR rather than fixed here. | +| Checkpoint capture reliability and shared-workspace rewind / `93e04160a0c0dece8a258384d2118a660415a53d` | `869347bc26051bba7c3c0274a7dfcae7770d3e3a` (#11665), `1455cb5c35e50211145073be6ca155d56684f0f4` (#12181), `67993623afb14c017bb0b959478dc892f9c427da` (#12307), `b4620d595554654c259ec7c0afb3d3d5532959a8` (#12306), `d17f46d7631b178b6834596264f0f44f210d584b` (#12308) | Adopted all five. Transient git exits retry during capture behind a new optional `retryable` hint on `VcsProcessExitError`; capture recovers nested repositories without commits under index-lock recovery and bounded probe timeouts; a failed baseline lookup logs and treats the ref as absent instead of losing the checkpoint; the `@`-mention entry refresh moves to a coalescing drainable worker chained into `CheckpointReactor.drain`. File rewind is refused unless the thread's cwd is its own worktree with no other thread or live provider session nested inside or above it, and web hides the destructive control for shared directories. **Capability removal, maintainer-approved 2026-09-18**: threads running in the project directory rewind the conversation only, with no override path. Pylon adaptation: the isolation check is scoped with `restoreFiles !== false` so conversation-only rewind still reaches Pylon's existing provider-capability explanation rather than the isolation message or silence, since Pylon's rollback saga services are optional. Pylon git identity, rollback revert action and wire compatibility preserved. No excluded source behavior; cursor unchanged. | [Checkpoint reliability #665](https://github.com/pylon-code/pylon/pull/665); 122 focused tests including three new `restoreFiles` regressions where none existed, `t3`/`contracts`/`web` typechecks, scoped lint (0 errors), rebased onto `origin/pylon` after #653. | +| Provider event log bounds and Codex image attachments / `93e04160a0c0dece8a258384d2118a660415a53d` | `b17cc2ab5d5029f898121798ca19db3288cba616` (#12305, partial), `3fd21df62da3191f1abf12a0cd74df6de9ed30a2` (#11050) | `3fd21df62d` adopted: Codex image attachments pass as `{ type: "localImage", path }` instead of base64 data URLs, so `turn/start` no longer scales with file size. `localImage` is a real content type in the generated Codex app-server schema; Pylon's attachment-id validation against `attachmentsDir` and its `FileSystem` wiring are retained. `b17cc2ab5d` **partially adopted**: record traversal is bounded before serialization with character, field and depth budgets, cycle detection, a `summarizeProviderEvent` fallback that keeps routing and failure fields, and a post-serialization byte-length fallback; new transient suppressions for `turn/diff/updated` and `stream_event`/`content_block_delta`. Pylon's `commitGuard` and existing transient filters preserved. **Excluded: the `stage === "raw"` suppression and `stage === "decoded"` unwrap** (see EXC-1). Cursor unchanged. | [Provider payload bounds #653](https://github.com/pylon-code/pylon/pull/653), merged `4be539f4d48af6ffbe545e015ec246fbb271c92e`; 133 focused tests, `t3` typecheck, scoped format/lint, all final-head CI jobs green, no bot findings. | +| PR comments, controls and avatars / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `f0a0ead946ca3f3f310460a91b7dabb0e3fe4834` (#11962), `f45c6b4a755905485431872f4a4188de3ed05974` (#11994), `c12ba7581ac00ba2a623ad23283da5d03d34c2d8` (#12150), `962bf62921c0ecbc9320f9b071e16b1d41484aad` (#12125), `df466c79810046d20c0f3ccc4e51709c5703814e` (#11728) | Adopted all five. Narrow controls, keyboard submission, paged bot/finished groups, long-comment previews, detail avatars and image-failure fallback. Pylon corrections align bot app links, lazily page finished groups, and scroll after collapsing. Optional actor metadata preserves wire compatibility; existing bounded GitHub enrichment/fallback limits remain. No excluded source behavior; cursor unchanged. | [PR review #621](https://github.com/pylon-code/pylon/pull/621); focused regressions, scoped types/lint, independent Opus adversarial reviews, browser before/after evidence and comment interaction recording. | +| Large diffs and review panel defaults / `6d1d549441be84f19696ab59ed7e2fbf305280d4` | `aae361c4ef6478e4dcf970d574f3fd38d908e6b9` (#10822), `36caf200c2b24c12838570966f5c85786a1fc1f8` (#11484), `3efdcc5296f1754e0f3bf7fee5fc2ada510e0438` (#11931), `6d1d549441be84f19696ab59ed7e2fbf305280d4` (#12190), `52ad70ec43b5dcfcdc72ac79c0964da8e4d163db` (#12142), `394af73e02238e551db14f44d57f1469dd2942aa` (#12139) | Adopted all six. Complete Git metadata and progressive per-file patches on web/mobile; preserve old-server preview fallback, literal paths and bounded output. Tree order/folder state survive refresh. Files default collapsed while explicit saved choices persist. Generic diff openings reset to working tree; linked PRs outrank automatic diffs. Preserve Pylon multi-PR authority, environment identity, responsive layout and existing settings. No excluded source behavior; cursor unchanged. | [Review diffs #616](https://github.com/pylon-code/pylon/pull/616); 509 integrated focused tests plus 79 unchanged backend regressions, six scoped package typechecks, lint, independent Antigravity adversarial reviews, before/after browser evidence and refresh video. Native verification details in PR. | +| Usage pools, hub quotas, reset credits and local composer action / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 16-source list in the usage pilot PR; parent `19d8ab2ae9fc562ee7b216a0d72903fbfafa9572`, final hub-routing fix `6abdf37a50ce6c1c9fabc499f4d0e159a6182d90` | Pylon port of the final behavior; parent partially adopted with native Claude scoped push mapping retained in DEF-16. `00d6109cbb1a13712d7347701969870aee83252d` is covered: demo fixtures were never introduced. Native Claude quota reads reuse Pylon's cached OAuth parser; no duplicate SDK probe or ingestion worker. | [Usage pilot #400](https://github.com/pylon-code/pylon/pull/400); focused quota, retry, RPC and compatibility checks, web/mobile evidence in PR. | +| Pull-request browsing, cached reads and opt-in panel behavior / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 16-source disposition list in #401; 15 adopted, #9877 deferred | Final PR state icons, authored/readiness sorting, seeded detail, keyboard pickers, link previews, profile links, revision-aware caches, project-filter deduplication and manual panel priority. Preserve Pylon server-projected PR tracking and quota reservation/refund semantics. Copy priority remains DEF-17. | [PR workflow #401](https://github.com/pylon-code/pylon/pull/401); 768 focused tests, affected package types, scoped lint and integrated web evidence. | +| Composer interaction, layout and notice follow-ups / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 27-source disposition list in #402 | Final scroll-only resting behavior, readable multiline drafts, stable control measurements, last-message reservation, narrow-width controls and opt-in context meter. Preserve Pylon provider locks, quick questions, session/harness/goal actions, quota placement, upload handling and diagnostic errors. Temporary blur-selection holding in #9499 is superseded by #10437; no unresolved scope in this group. | [Composer #402](https://github.com/pylon-code/pylon/pull/402); 724 focused tests, six affected package typechecks, scoped lint, integrated web screenshots and motion evidence. | +| Changed-file trees and stable diff navigation / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full eight-source list in #403; seven new sources and DEF-11 | Adopted: persistent folder trees, diff/PR trees, repeated selection, saved layout, wrapped edit heights, search focus and folder sorting. Completes DEF-11 after removing all helper callers. Pylon lazy-viewer scope guards remain; narrow panels place the tree below code. | [File navigation #403](https://github.com/pylon-code/pylon/pull/403); 271 focused tests, six package typechecks, scoped lint/export checks and integrated web evidence. | +| Thread link targets / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Three full source SHAs in [#404](https://github.com/pylon-code/pylon/pull/404). | Adopted link target preference across chat, terminal and PR surfaces; completed DEF-12 and DEF-15 after replacing their last callers. Preserve Pylon context/routing, browser defaults, safe errors and external fallback. Browser profiles remain an independent source group. | [#404](https://github.com/pylon-code/pylon/pull/404); 233 focused tests, seven package typechecks, targeted lint/export checks; web evidence in PR. | | Document and media preview integration / `062987b2fb0ef48cc7776d654931bd9f3bcf1f9f` | Full 13-source list in #405, including DEF-14 | Twelve adopted sources; #9460 is already covered by Pylon’s expanded-only viewed-image rows. Shared parsing, document attachment tabs, preview retry states, draft file readers, native SVG and short-source scrolling. Preserve image dimensions, POSIX case, explicit panel choices, video expansion and local-file leases. Completes DEF-14 after removing all predicate callers. | [Media previews #405](https://github.com/pylon-code/pylon/pull/405); focused cross-client tests, seven package typechecks, native iOS build and client evidence in PR. | @@ -167,9 +167,9 @@ All three sources in the range `d1d15c67f4a5fb82fd8d5e01e5e3b288296789c3..a43f9b One open functional deferral, DEF-18, and one open exclusion, EXC-1, are recorded below with their revisit triggers. DEF-7 and DEF-16 are completed above; remaining exact native rollback compatibility limits are documented above. Revisit a limit when the native provider exposes the missing proof or fork behavior; do not substitute relative counts. Historical reasons and revisit conditions remain in the archive and earlier cycle records. -| Deferral | Sources | Remaining behavior and reason | Opened | Revisit trigger | -| ------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| DEF-18: activity-group timeline refinements | `592021f008010ee947c326df0452d093ead8aeb1` (#12302), `fbc8b960026e408c163ea55db8075bbf1a42d628` (#12270) | Both refine upstream's activity-group timeline: thoughts collapse inside a tool group behind their own disclosure, and a failed tool call stays in the same activity row instead of ending the live run. Neither is portable — Pylon has no `ActivityGroupTimelineRow`, `ReasoningTraceBlock`, `expandedReasoningMessageIds`, `onToggleReasoning`, `isActivityEntry` or `reasoningMessages`, and `packages/contracts/src/orchestration.ts` has no reasoning fields at all. Pylon's `ThinkingTimelineRow` is a transient indicator, not persisted provider reasoning. Adopting them first requires `052c7ae53e46c705d5fd5dfa995275bc3100744e` (#11784, 32 files / 1,923 insertions, including contracts, client-runtime and a settings toggle) and `22539f2a428947ab218589c984b2af44e434012e` (#12147, web + mobile) — a new full-stack capability, not a catch-up adaptation. **Parked behind the orchestration upgrade at the maintainer's decision on 2026-09-19**: orchestration v2 is unmerged on `t3code-upstream/t3code/codex-turn-mapping` (442 commits off `main`, last touched 2026-09-12) and will rewrite orchestration contracts, so main-line reasoning fields adopted now risk rework. There is no orchestration v3 upstream. | 2026-09-19 | Pylon resolves the orchestration v2 adoption question, or that line merges upstream: `git merge-base --is-ancestor t3code-upstream/t3code/codex-turn-mapping t3code-upstream/main`. Also triggered if Pylon adopts the activity-group timeline independently — check for `ActivityGroupTimelineRow` in `apps/web/src/components/chat/MessagesTimeline.tsx`. Dependency-driven, not time-driven; no earliest date applies. | +| Deferral | Sources | Remaining behavior and reason | Opened | Revisit trigger | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| DEF-18: activity-group timeline refinements | `592021f008010ee947c326df0452d093ead8aeb1` (#12302), `fbc8b960026e408c163ea55db8075bbf1a42d628` (#12270), `dfbb11bdd7c3f1a5575cb55d3e3abb12be025727` (#12377) | The first two refine upstream's activity-group timeline: thoughts collapse inside a tool group behind their own disclosure, and a failed tool call stays in the same activity row instead of ending the live run. The third renders plain text in collapsed thought previews. These depend on provider reasoning and activity grouping that Pylon has not adopted; none is portable independently — Pylon has no `ActivityGroupTimelineRow`, `ReasoningTraceBlock`, `expandedReasoningMessageIds`, `onToggleReasoning`, `isActivityEntry` or `reasoningMessages`, and `packages/contracts/src/orchestration.ts` has no reasoning fields at all. Pylon's `ThinkingTimelineRow` is a transient indicator, not persisted provider reasoning. Adopting them first requires `052c7ae53e46c705d5fd5dfa995275bc3100744e` (#11784, 32 files / 1,923 insertions, including contracts, client-runtime and a settings toggle) and `22539f2a428947ab218589c984b2af44e434012e` (#12147, web + mobile) — a new full-stack capability, not a catch-up adaptation. **Parked behind the orchestration upgrade at the maintainer's decision on 2026-09-19**: orchestration v2 is unmerged on `t3code-upstream/t3code/codex-turn-mapping` (442 commits off `main`, last touched 2026-09-12) and will rewrite orchestration contracts, so main-line reasoning fields adopted now risk rework. There is no orchestration v3 upstream. | 2026-09-19 | Pylon resolves the orchestration v2 adoption question, or that line merges upstream: `git merge-base --is-ancestor t3code-upstream/t3code/codex-turn-mapping t3code-upstream/main`. Also triggered if Pylon adopts the activity-group timeline independently — check for `ActivityGroupTimelineRow` in `apps/web/src/components/chat/MessagesTimeline.tsx`. Dependency-driven, not time-driven; no earliest date applies. | | Exclusion | Source | Excluded behavior and reason | Opened | Revisit trigger | | ------------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | From dcee43caf7e4660ca3597bf3e43209fe743e6a9d Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 22:53:12 -0600 Subject: [PATCH 072/104] fix(web): make provider accent color controls keyboard accessible (#805) --- .../ProviderAccentColorPicker.test.tsx | 95 +++++++++++++++++++ .../settings/ProviderAccentColorPicker.tsx | 80 +++++++++++++++- 2 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/components/settings/ProviderAccentColorPicker.test.tsx diff --git a/apps/web/src/components/settings/ProviderAccentColorPicker.test.tsx b/apps/web/src/components/settings/ProviderAccentColorPicker.test.tsx new file mode 100644 index 0000000000..a17bf73d9a --- /dev/null +++ b/apps/web/src/components/settings/ProviderAccentColorPicker.test.tsx @@ -0,0 +1,95 @@ +import { act, type ReactNode } from "react"; +import { create, type ReactTestRenderer } from "react-test-renderer"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; + +vi.mock("../ui/popover", () => ({ + Popover: ({ children }: { children: ReactNode }) => children, + PopoverPopup: ({ children }: { children: ReactNode }) => children, + PopoverTrigger: () => null, + PopoverClose: () => null, +})); + +import { ProviderAccentColorPicker } from "./ProviderAccentColorPicker"; + +let renderer: ReactTestRenderer | undefined; + +beforeEach(() => vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true)); + +afterEach(async () => { + await act(async () => renderer?.unmount()); + renderer = undefined; + vi.unstubAllGlobals(); +}); + +function control(label: string) { + return renderer!.root.find( + (node) => + (node.props.role === "slider" || node.props.type === "range") && + node.props["aria-label"] === label, + ); +} + +async function press(label: string, key: string, shiftKey = false) { + const preventDefault = vi.fn(); + await act(async () => control(label).props.onKeyDown({ key, shiftKey, preventDefault })); + return preventDefault; +} + +describe("provider accent color keyboard controls", () => { + it("adjusts hue and both plane axes through focusable controls without changing unrelated axes", async () => { + const onCommit = vi.fn(); + await act(async () => { + renderer = create( + , + ); + }); + const hue = "Accent color hue"; + const saturation = "Accent color saturation"; + const brightness = "Accent color brightness"; + expect(control(hue).props.tabIndex).toBe(0); + expect(control(saturation).props.type).toBe("range"); + expect(control(brightness).props.type).toBe("range"); + + expect(await press(hue, "ArrowLeft")).toHaveBeenCalledOnce(); + expect(control(hue).props["aria-valuenow"]).toBe(359); + expect(await press(hue, "ArrowRight")).toHaveBeenCalledOnce(); + expect(onCommit).toHaveBeenLastCalledWith("#ff0000"); + + await press(saturation, "ArrowLeft", true); + expect(control(saturation).props["aria-valuetext"]).toBe("90%"); + expect(control(brightness).props["aria-valuetext"]).toBe("100%"); + expect(onCommit).toHaveBeenLastCalledWith("#ff1919"); + await press(brightness, "ArrowDown", true); + expect(control(saturation).props["aria-valuetext"]).toBe("90%"); + expect(control(brightness).props["aria-valuetext"]).toBe("90%"); + expect(onCommit).toHaveBeenLastCalledWith("#e61717"); + expect(control(hue).props["aria-valuenow"]).toBe(0); + + onCommit.mockClear(); + expect(await press(saturation, "Tab")).not.toHaveBeenCalled(); + expect(onCommit).not.toHaveBeenCalled(); + }); + + it("supports native range changes and Home/End per color channel", async () => { + const onCommit = vi.fn(); + await act(async () => { + renderer = create( + , + ); + }); + const saturation = "Accent color saturation"; + const brightness = "Accent color brightness"; + await press(saturation, "Home"); + expect(control(saturation).props.value).toBe(0); + expect(onCommit).toHaveBeenLastCalledWith("#ffffff"); + await act(async () => + control(brightness).props.onChange({ currentTarget: { valueAsNumber: 50 } }), + ); + expect(control(brightness).props.value).toBe(50); + expect(onCommit).toHaveBeenLastCalledWith("#808080"); + await press(saturation, "End"); + expect(control(saturation).props.value).toBe(100); + await press("Accent color hue", "End"); + expect(control("Accent color hue").props["aria-valuenow"]).toBe(359); + }); +}); diff --git a/apps/web/src/components/settings/ProviderAccentColorPicker.tsx b/apps/web/src/components/settings/ProviderAccentColorPicker.tsx index 85cb69e002..2c28f0e5a0 100644 --- a/apps/web/src/components/settings/ProviderAccentColorPicker.tsx +++ b/apps/web/src/components/settings/ProviderAccentColorPicker.tsx @@ -1,7 +1,15 @@ "use client"; import { PipetteIcon, XIcon } from "lucide-react"; -import { useCallback, useEffect, useMemo, useRef, useState, type PointerEvent } from "react"; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, + type KeyboardEvent, + type PointerEvent, +} from "react"; import { Button } from "../ui/button"; import { Popover, PopoverClose, PopoverPopup, PopoverTrigger } from "../ui/popover"; @@ -106,6 +114,36 @@ function ProviderCustomColorPanel(props: { [commitHsv, hsv], ); + const handlePlaneAxisKeyDown = (event: KeyboardEvent, axis: "s" | "v") => { + const step = event.shiftKey ? 0.1 : 0.02; + let next: number; + if (event.key === "ArrowLeft" || event.key === "ArrowDown") next = clamp(hsv[axis] - step); + else if (event.key === "ArrowRight" || event.key === "ArrowUp") next = clamp(hsv[axis] + step); + else if (event.key === "Home") next = 0; + else if (event.key === "End") next = 1; + else return; + event.preventDefault(); + commitHsv({ ...hsv, [axis]: next }); + }; + + const handleHueKeyDown = (event: KeyboardEvent) => { + if (event.key === "Home") { + event.preventDefault(); + commitHsv({ ...hsv, h: 0 }); + return; + } + if (event.key === "End") { + event.preventDefault(); + commitHsv({ ...hsv, h: 359 }); + return; + } + if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)) return; + event.preventDefault(); + const direction = event.key === "ArrowLeft" || event.key === "ArrowDown" ? -1 : 1; + const step = event.shiftKey ? 10 : 1; + commitHsv({ ...hsv, h: (hsv.h + direction * step + 360) % 360 }); + }; + const handlePointerDown = (handler: (event: PointerEvent) => void) => { return (event: PointerEvent) => { event.currentTarget.setPointerCapture(event.pointerId); @@ -116,7 +154,9 @@ function ProviderCustomColorPanel(props: { return (
+ {(["s", "v"] as const).map((axis) => { + const label = axis === "s" ? "Saturation" : "Brightness"; + return ( + + ); + })}
{ if (event.currentTarget.hasPointerCapture(event.pointerId)) { updateFromHue(event); From f3881cc08fb042de6600a5a01a68292d044a31ef Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 22:54:11 -0600 Subject: [PATCH 073/104] fix(web): show pull request state in context chips (#812) --- .../src/components/contextChipParts.test.tsx | 43 ++++++++++++++++++- apps/web/src/components/contextChipParts.tsx | 10 +++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/contextChipParts.test.tsx b/apps/web/src/components/contextChipParts.test.tsx index 13ecc7bfaa..b5a396f628 100644 --- a/apps/web/src/components/contextChipParts.test.tsx +++ b/apps/web/src/components/contextChipParts.test.tsx @@ -1,7 +1,14 @@ import type { ReactNode } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it, vi } from "vite-plus/test"; -import { ComposerContextScope, ContextChipPopover } from "./contextChipParts"; +import { ComposerContextScope, ContextChipPopover, PullRequestChip } from "./contextChipParts"; + +vi.mock("~/lib/openPullRequestLink", () => ({ usePullRequestPreviewTarget: () => null })); +vi.mock("./ui/tooltip", () => ({ + Tooltip: ({ children }: { children: ReactNode }) => children, + TooltipTrigger: ({ render }: { render: ReactNode }) => render, + TooltipPopup: ({ children }: { children: ReactNode }) => children, +})); vi.mock("./ui/popover", () => ({ Popover: ({ children }: { children: ReactNode }) => children, @@ -27,3 +34,37 @@ describe("context detail popup ownership", () => { }, ); }); + +describe("pull request context chip", () => { + it.each([ + { state: "open", isDraft: false, label: "Open", icon: "lucide-git-pull-request-arrow" }, + { state: "open", isDraft: true, label: "Draft", icon: "lucide-git-pull-request-draft" }, + { state: "closed", isDraft: true, label: "Closed", icon: "lucide-git-pull-request-closed" }, + { state: "merged", isDraft: true, label: "Merged", icon: "lucide-git-merge" }, + ] as const)( + "shows $label state in icon and accessible name", + ({ state, isDraft, label, icon }) => { + const markup = renderToStaticMarkup( + undefined} + />, + ); + expect(markup).toContain(`(${label})`); + expect(markup).toContain(icon); + }, + ); +}); diff --git a/apps/web/src/components/contextChipParts.tsx b/apps/web/src/components/contextChipParts.tsx index 8ce41fe878..5b0ce5a03d 100644 --- a/apps/web/src/components/contextChipParts.tsx +++ b/apps/web/src/components/contextChipParts.tsx @@ -6,7 +6,7 @@ import { useComposerMenuProps } from "./chat/composerEventScope"; export const ComposerContextScope = createContext(false); import { cn } from "~/lib/utils"; -import { PullRequestGlyph } from "~/components/pullRequest/pullRequestIcons"; +import { resolvePullRequestState } from "~/components/pullRequest/pullRequestPresentation"; import { PierreEntryIcon } from "./chat/PierreEntryIcon"; import { COMPOSER_INLINE_CHIP_ICON_CLASS_NAME, @@ -123,6 +123,7 @@ export function PullRequestChip(props: { onOpen: (event: MouseEvent, url: string) => void; }) { const previewTarget = usePullRequestPreviewTarget(props.environmentId, props.metadata.url); + const state = resolvePullRequestState(props.metadata); const button = ( From ecbf4e5dd922bf89f7cc183e1ef6df6ac9668361 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 22:55:17 -0600 Subject: [PATCH 074/104] fix(web): keep media controls clear and restore gallery arrows (#814) --- .../chat/ExpandedImageDialog.test.tsx | 85 ++++++++++++++++++- .../components/chat/ExpandedImageDialog.tsx | 80 ++++++++++------- .../components/chat/ZoomableImage.test.tsx | 64 ++++++++++++++ .../web/src/components/chat/ZoomableImage.tsx | 16 +++- 4 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 apps/web/src/components/chat/ZoomableImage.test.tsx diff --git a/apps/web/src/components/chat/ExpandedImageDialog.test.tsx b/apps/web/src/components/chat/ExpandedImageDialog.test.tsx index d2593143d7..69c5a9e55a 100644 --- a/apps/web/src/components/chat/ExpandedImageDialog.test.tsx +++ b/apps/web/src/components/chat/ExpandedImageDialog.test.tsx @@ -1,4 +1,4 @@ -import { act, type ReactNode } from "react"; +import { act, type KeyboardEventHandler, type ReactNode } from "react"; import { create, type ReactTestRenderer } from "react-test-renderer"; import { afterEach, describe, expect, it, vi } from "vite-plus/test"; import { ExpandedImageDialog } from "./ExpandedImageDialog"; @@ -20,7 +20,17 @@ vi.mock("../ui/dialog", () => ({ state.change = onOpenChange; return children; }, - DialogPopup: ({ children }: { children: ReactNode }) =>
{children}
, + DialogPopup: ({ + children, + onKeyDown, + }: { + children: ReactNode; + onKeyDown: KeyboardEventHandler; + }) => ( +
+ {children} +
+ ), DialogTitle: ({ children }: { children: ReactNode }) =>

{children}

, })); vi.mock("../ui/button", () => ({ @@ -86,4 +96,75 @@ describe("expanded media dismissal", () => { state.change?.(false, { reason: "escape-key", cancel }); expect(close).toHaveBeenCalledOnce(); }); + + it("navigates gallery arrows inside the dialog and leaves video and snapshot keys alone", async () => { + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + vi.stubGlobal( + "HTMLElement", + class HTMLElement { + tagName = "PRE"; + }, + ); + vi.stubGlobal("HTMLVideoElement", function HTMLVideoElement() {}); + vi.stubGlobal("document", { activeElement: null }); + vi.stubGlobal("window", { addEventListener: vi.fn(), removeEventListener: vi.fn() }); + const close = vi.fn(); + await act(() => { + renderer = create( + , + ); + }); + const dialog = renderer!.root.find((node) => node.props["data-test-dialog-popup"] === true); + const press = async (key: string, target: object) => { + const preventDefault = vi.fn(); + const stopPropagation = vi.fn(); + await act(() => { + dialog.props.onKeyDown({ + key, + target, + defaultPrevented: false, + preventDefault, + stopPropagation, + }); + }); + return { preventDefault, stopPropagation }; + }; + const videoKey = await press("ArrowRight", new HTMLVideoElement()); + expect(videoKey.preventDefault).not.toHaveBeenCalled(); + expect( + renderer!.root.findAll((node) => node.type === "img" && node.props.alt === "preview"), + ).toHaveLength(1); + + const snapshotKey = await press("ArrowRight", new HTMLElement()); + expect(snapshotKey.preventDefault).not.toHaveBeenCalled(); + expect( + renderer!.root.findAll( + (node) => node.type === "span" && node.children.join("").includes("one.png"), + ), + ).not.toHaveLength(0); + + const next = await press("ArrowRight", {}); + expect(next.preventDefault).toHaveBeenCalledOnce(); + expect(next.stopPropagation).toHaveBeenCalledOnce(); + expect( + renderer!.root.findAll( + (node) => node.type === "button" && node.props["aria-label"] === "Previous media", + ), + ).toHaveLength(1); + expect( + renderer!.root.findAll( + (node) => node.type === "span" && node.children.join("").includes("two.png"), + ), + ).not.toHaveLength(0); + expect(close).not.toHaveBeenCalled(); + }); }); diff --git a/apps/web/src/components/chat/ExpandedImageDialog.tsx b/apps/web/src/components/chat/ExpandedImageDialog.tsx index e352d4cb17..0dc131149b 100644 --- a/apps/web/src/components/chat/ExpandedImageDialog.tsx +++ b/apps/web/src/components/chat/ExpandedImageDialog.tsx @@ -1,4 +1,12 @@ -import { memo, useCallback, useEffect, useRef, useState, type ReactNode } from "react"; +import { + memo, + useCallback, + useEffect, + useRef, + useState, + type ReactNode, + type KeyboardEvent, +} from "react"; import { ChevronLeftIcon, ChevronRightIcon, ImageIcon, TextIcon, XIcon } from "lucide-react"; import { Button } from "../ui/button"; import { Dialog, DialogPopup, DialogTitle } from "../ui/dialog"; @@ -29,7 +37,7 @@ interface ExpandedImageDialogProps { } const EXPANDED_MEDIA_STATE_CLASS_NAME = - "flex aspect-auto h-48 min-h-0 w-[min(92vw,32rem)] flex-col items-center justify-center gap-3 rounded-lg border border-border/70 bg-black p-6 text-center text-sm text-white shadow-2xl"; + "flex aspect-auto h-48 min-h-0 w-[min(var(--media-width),32rem)] flex-col items-center justify-center gap-3 rounded-lg border border-border/70 bg-black p-6 text-center text-sm text-white shadow-2xl"; function ExpandedMediaFailure({ children }: { children: ReactNode }) { return ( @@ -59,8 +67,8 @@ function ExpandedVideo({ item }: { readonly item: ExpandedImageItem }) { originalUrl={item.originalUrl} preload="metadata" autoPlay={item.autoPlay ?? true} - className="block max-h-[86vh] max-w-[92vw] text-center" - videoClassName="aspect-auto max-h-[86vh] w-auto max-w-[92vw] rounded-lg border border-border/70 shadow-2xl" + className="block max-h-[var(--media-height)] max-w-[var(--media-width)] text-center" + videoClassName="aspect-auto max-h-[var(--media-height)] w-auto max-w-[var(--media-width)] rounded-lg border border-border/70 shadow-2xl" stateClassName={EXPANDED_MEDIA_STATE_CLASS_NAME} onRetry={asset ? refreshAssetUrl : undefined} /> @@ -118,29 +126,31 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ }; }, []); - useEffect(() => { - const onKeyDown = (event: globalThis.KeyboardEvent) => { - if (event.defaultPrevented || isContextMenuOpen()) return; - if (zoomableImageRef.current?.pan(event.key)) { - event.preventDefault(); - event.stopPropagation(); - return; - } - if (preview.images.length <= 1) return; - if (event.key === "ArrowLeft") { - event.preventDefault(); - event.stopPropagation(); - navigateImage(-1); - return; - } - if (event.key !== "ArrowRight") return; + const onKeyDown = (event: KeyboardEvent) => { + if ( + event.defaultPrevented || + isContextMenuOpen() || + event.target instanceof HTMLVideoElement || + (event.target instanceof HTMLElement && event.target.tagName === "PRE") + ) + return; + if (zoomableImageRef.current?.pan(event.key)) { event.preventDefault(); event.stopPropagation(); - navigateImage(1); - }; - window.addEventListener("keydown", onKeyDown); - return () => window.removeEventListener("keydown", onKeyDown); - }, [navigateImage, preview.images.length]); + return; + } + if (preview.images.length <= 1) return; + if (event.key === "ArrowLeft") { + event.preventDefault(); + event.stopPropagation(); + navigateImage(-1); + return; + } + if (event.key !== "ArrowRight") return; + event.preventDefault(); + event.stopPropagation(); + navigateImage(1); + }; if (!item) return null; const mediaLabel = item.type === "video" ? "video" : "image"; @@ -176,9 +186,13 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ bottomStickOnMobile={false} backdropClassName="z-[60]" viewportClassName="z-[60] grid-rows-1 place-items-center px-4 py-6 [-webkit-app-region:no-drag]" - className="row-start-1 max-h-[92vh] w-auto max-w-[92vw] overflow-visible" + className="row-start-1 max-h-[92vh] w-[92vw] max-w-[92vw] items-center overflow-visible [--media-width:92vw] [--media-height:min(86vh,calc(100vh-160px))] sm:[--media-width:calc(92vw-96px)]" + onKeyDown={onKeyDown} initialFocus={closeButtonRef} finalFocus={() => returnFocusTarget} + onClick={(event) => { + if (event.target === event.currentTarget) onClose(); + }} > Expanded {mediaLabel} preview {preview.images.length > 1 && ( @@ -186,7 +200,7 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ type="button" size="icon" variant="media-navigation" - className="left-2 sm:left-6" + className="left-0 top-auto -bottom-12 translate-y-0 rounded-full bg-white/10 sm:top-1/2 sm:bottom-auto sm:-translate-y-1/2" aria-label="Previous media" onClick={() => navigateImage(-1)} > @@ -194,13 +208,13 @@ export const ExpandedImageDialog = memo(function ExpandedImageDialog({ )} -
+
+ ) : null; const environmentReconnecting = unavailableConnection !== null && (unavailableConnection.phase === "connecting" || @@ -2799,6 +2879,7 @@ export default function ChatView(props: ChatViewProps) { ), title: `${unavailableConnection.phase === "connecting" ? "Connecting" : "Reconnecting"} to ${activeEnvironmentUnavailableState.label}`, description: "Finishing an update", + actions: disconnectAction, compact: true, }); } else { @@ -2831,6 +2912,7 @@ export default function ChatView(props: ChatViewProps) { > Connections + {disconnectAction} ), }); @@ -2885,22 +2967,24 @@ export default function ChatView(props: ChatViewProps) { (versionMismatchSelfUpdate !== "desktop-managed" || !versionMismatchDesktopAppUpdate) ? serverUpdateGuidance(versionMismatchSelfUpdate) : undefined, - actions: - updateInProgress || - !versionMismatch || + actions: updateInProgress ? ( + suppressUnavailableBanner ? ( + disconnectAction + ) : undefined + ) : !versionMismatch || (versionMismatchSelfUpdate === "desktop-managed" && !versionMismatchDesktopAppUpdate) ? undefined : ( - - ), + + ), ...(updateInProgress || (!updateFailed && !versionMismatchDismissKey) ? {} : { @@ -2928,6 +3012,9 @@ export default function ChatView(props: ChatViewProps) { activeEnvironmentUnavailableState, reconnectWarningGraceElapsed, handleReconnectActiveEnvironment, + canDisconnectActiveEnvironment, + disconnectingEnvironmentId, + handleDisconnectActiveEnvironment, navigate, setDismissedVersionMismatchKey, showVersionMismatchBanner, diff --git a/apps/web/src/hooks/useEnvironmentDisconnectDelay.test.tsx b/apps/web/src/hooks/useEnvironmentDisconnectDelay.test.tsx new file mode 100644 index 0000000000..f584112df4 --- /dev/null +++ b/apps/web/src/hooks/useEnvironmentDisconnectDelay.test.tsx @@ -0,0 +1,65 @@ +import { EnvironmentId } from "@t3tools/contracts"; +import { act, useLayoutEffect } from "react"; +import { create, type ReactTestRenderer } from "react-test-renderer"; +import { afterEach, beforeEach, expect, it, vi } from "vite-plus/test"; + +import { useEnvironmentDisconnectDelay } from "./useEnvironmentDisconnectDelay"; + +const environmentId = EnvironmentId.make("remote"); +let renderer: ReactTestRenderer; +let elapsed = false; + +function Probe({ unavailableId }: { unavailableId: EnvironmentId | null }) { + const value = useEnvironmentDisconnectDelay(unavailableId); + useLayoutEffect(() => { + elapsed = value; + }); + return null; +} + +beforeEach(() => { + vi.useFakeTimers(); + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + act(() => { + renderer = create(); + }); +}); + +afterEach(() => { + act(() => renderer.unmount()); + vi.useRealTimers(); + vi.unstubAllGlobals(); +}); + +it("waits 20 seconds without restarting on renders for the same environment", () => { + act(() => vi.advanceTimersByTime(10_000)); + expect(elapsed).toBe(false); + act(() => renderer.update()); + act(() => vi.advanceTimersByTime(9_999)); + expect(elapsed).toBe(false); + act(() => vi.advanceTimersByTime(1)); + expect(elapsed).toBe(true); +}); + +it("cancels a brief outage and starts a fresh delay on the next outage", () => { + act(() => vi.advanceTimersByTime(10_000)); + act(() => renderer.update()); + act(() => vi.advanceTimersByTime(20_000)); + expect(elapsed).toBe(false); + act(() => renderer.update()); + act(() => vi.advanceTimersByTime(19_999)); + expect(elapsed).toBe(false); + act(() => vi.advanceTimersByTime(1)); + expect(elapsed).toBe(true); + act(() => renderer.update()); + expect(elapsed).toBe(false); +}); + +it("does not carry elapsed time to another environment", () => { + act(() => vi.advanceTimersByTime(20_000)); + expect(elapsed).toBe(true); + act(() => renderer.update()); + expect(elapsed).toBe(false); + act(() => vi.advanceTimersByTime(20_000)); + expect(elapsed).toBe(true); +}); diff --git a/apps/web/src/hooks/useEnvironmentDisconnectDelay.ts b/apps/web/src/hooks/useEnvironmentDisconnectDelay.ts new file mode 100644 index 0000000000..17c6f8f87b --- /dev/null +++ b/apps/web/src/hooks/useEnvironmentDisconnectDelay.ts @@ -0,0 +1,25 @@ +import type { EnvironmentId } from "@t3tools/contracts"; +import { useEffect, useState } from "react"; + +/** Wait through brief outages before offering to switch off the active environment. */ +export function useEnvironmentDisconnectDelay(unavailableEnvironmentId: EnvironmentId | null) { + const [delay, setDelay] = useState({ environmentId: unavailableEnvironmentId, elapsed: false }); + if (delay.environmentId !== unavailableEnvironmentId) { + setDelay({ environmentId: unavailableEnvironmentId, elapsed: false }); + } + + useEffect(() => { + if (unavailableEnvironmentId === null) return; + const timeout = setTimeout( + () => setDelay({ environmentId: unavailableEnvironmentId, elapsed: true }), + 20_000, + ); + return () => clearTimeout(timeout); + }, [unavailableEnvironmentId]); + + return ( + unavailableEnvironmentId !== null && + delay.environmentId === unavailableEnvironmentId && + delay.elapsed + ); +} diff --git a/apps/web/src/lib/environmentDisconnectEligibility.ts b/apps/web/src/lib/environmentDisconnectEligibility.ts new file mode 100644 index 0000000000..00ef16cd39 --- /dev/null +++ b/apps/web/src/lib/environmentDisconnectEligibility.ts @@ -0,0 +1,16 @@ +import type { EnvironmentPresentation } from "@t3tools/client-runtime/connection"; + +import { isDesktopLocalConnectionTarget } from "../connection/desktopLocal"; + +/** Recheck the live catalog and connection before honoring a delayed UI action. */ +export function canManuallyDisconnectEnvironment( + presentation: EnvironmentPresentation | null, +): boolean { + return ( + presentation !== null && + presentation.entry.enabled && + presentation.entry.target._tag !== "PrimaryConnectionTarget" && + !isDesktopLocalConnectionTarget(presentation.entry.target) && + presentation.connection.phase !== "connected" + ); +} diff --git a/apps/web/src/lib/environmentDisconnectNavigation.test.ts b/apps/web/src/lib/environmentDisconnectNavigation.test.ts new file mode 100644 index 0000000000..02cfb9e6bc --- /dev/null +++ b/apps/web/src/lib/environmentDisconnectNavigation.test.ts @@ -0,0 +1,68 @@ +import { describe, expect, it, vi } from "vite-plus/test"; + +import { + disconnectEnvironmentAndNavigate, + disconnectIfStillEligible, +} from "./environmentDisconnectNavigation"; + +describe("disconnectEnvironmentAndNavigate", () => { + it("drops a stale button click when live eligibility changed before mutation", async () => { + let eligible = true; + const disconnect = vi.fn(async () => "disabled"); + const readLiveEligibility = () => eligible; + eligible = false; // Connection recovered after the last render, before the click. + expect( + await disconnectIfStillEligible({ isEligibleNow: readLiveEligibility, disconnect }), + ).toBeNull(); + expect(disconnect).not.toHaveBeenCalled(); + eligible = true; + expect( + await disconnectIfStillEligible({ isEligibleNow: readLiveEligibility, disconnect }), + ).toBe("disabled"); + expect(disconnect).toHaveBeenCalledOnce(); + }); + it("navigates home after a successful disconnect only while the same route is current", async () => { + let complete!: (result: { _tag: "Success" }) => void; + let location = { href: "/remote/thread", key: "first" }; + const navigateHome = vi.fn(); + const pending = disconnectEnvironmentAndNavigate({ + disconnect: () => new Promise<{ _tag: "Success" }>((resolve) => (complete = resolve)), + readLocation: () => location, + navigateHome, + }); + complete({ _tag: "Success" }); + await pending; + expect(navigateHome).toHaveBeenCalledOnce(); + + const delayed = disconnectEnvironmentAndNavigate({ + disconnect: () => new Promise<{ _tag: "Success" }>((resolve) => (complete = resolve)), + readLocation: () => location, + navigateHome, + }); + location = { href: "/remote/thread?panel=files", key: "first" }; + complete({ _tag: "Success" }); + await delayed; + expect(navigateHome).toHaveBeenCalledOnce(); + + const returned = disconnectEnvironmentAndNavigate({ + disconnect: () => new Promise<{ _tag: "Success" }>((resolve) => (complete = resolve)), + readLocation: () => location, + navigateHome, + }); + location = { href: "/remote/thread?panel=files", key: "second" }; + complete({ _tag: "Success" }); + await returned; + expect(navigateHome).toHaveBeenCalledOnce(); + }); + + it("keeps the current route after a failed disconnect", async () => { + const navigateHome = vi.fn(); + const result = await disconnectEnvironmentAndNavigate({ + disconnect: async () => ({ _tag: "Failure" as const }), + readLocation: () => ({ href: "/remote/thread", key: "first" }), + navigateHome, + }); + expect(result._tag).toBe("Failure"); + expect(navigateHome).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/web/src/lib/environmentDisconnectNavigation.ts b/apps/web/src/lib/environmentDisconnectNavigation.ts new file mode 100644 index 0000000000..6067c910fe --- /dev/null +++ b/apps/web/src/lib/environmentDisconnectNavigation.ts @@ -0,0 +1,32 @@ +interface LocationIdentity { + readonly href: string; + readonly key: string | undefined; +} + +/** The menu can remain visible for a render after a connection changes. */ +export async function disconnectIfStillEligible(input: { + readonly isEligibleNow: () => boolean; + readonly disconnect: () => Promise; +}): Promise { + if (!input.isEligibleNow()) return null; + return input.disconnect(); +} + +/** Keep a delayed disconnect from replacing a newer route or search state. */ +export async function disconnectEnvironmentAndNavigate(input: { + readonly disconnect: () => Promise; + readonly readLocation: () => LocationIdentity; + readonly navigateHome: () => void; +}): Promise { + const clickedLocation = input.readLocation(); + const result = await input.disconnect(); + const currentLocation = input.readLocation(); + if ( + result._tag === "Success" && + currentLocation.href === clickedLocation.href && + currentLocation.key === clickedLocation.key + ) { + input.navigateHome(); + } + return result; +} From a3150e121f37371db8c9f0846097850c5ac2464a Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:08:12 -0600 Subject: [PATCH 078/104] fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. --- .../Layers/CodexSessionRuntime.test.ts | 68 +++++++++++++++++++ .../provider/Layers/CodexSessionRuntime.ts | 27 ++++++++ docs/user/triage.md | 9 +++ 3 files changed, 104 insertions(+) diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts index 3cbf840237..3a509c4f08 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.test.ts @@ -16,6 +16,7 @@ import { describeMcpElicitation, hasConfiguredMcpServer, isRecoverableThreadResumeError, + isThreadWriterLockedError, makeMemoryConsolidationNotificationFilter, openCodexThread, readCodexThread, @@ -871,6 +872,44 @@ describe("isRecoverableThreadResumeError", () => { }); }); +describe("isThreadWriterLockedError", () => { + it("matches the app-server writer-lock refusal", () => { + NodeAssert.equal( + isThreadWriterLockedError( + new CodexErrors.CodexAppServerRequestError({ + code: -32603, + errorMessage: "thread 01a0ccd7-23c1-7c00-9cbd-eafe286d36b5 already has an active writer", + }), + ), + true, + ); + }); + + it("ignores other resume failures", () => { + NodeAssert.equal( + isThreadWriterLockedError( + new CodexErrors.CodexAppServerRequestError({ + code: -32603, + errorMessage: "thread not found", + }), + ), + false, + ); + }); + + it("is not treated as a recoverable resume error", () => { + NodeAssert.equal( + isRecoverableThreadResumeError( + new CodexErrors.CodexAppServerRequestError({ + code: -32603, + errorMessage: "thread 01a0ccd7-23c1-7c00-9cbd-eafe286d36b5 already has an active writer", + }), + ), + false, + ); + }); +}); + describe("openCodexThread", () => { it.effect("resumes metadata when historical turns contain unknown error values", () => Effect.gen(function* () { @@ -1097,4 +1136,33 @@ describe("openCodexThread", () => { NodeAssert.equal(error.errorMessage, "timed out waiting for server"); }), ); + + it.effect("explains a writer-locked conversation without starting a fresh thread", () => + Effect.gen(function* () { + const refusal = new CodexErrors.CodexAppServerRequestError({ + code: -32603, + errorMessage: "thread 01a0ccd7-23c1-7c00-9cbd-eafe286d36b5 already has an active writer", + }); + const client = { + request: () => Effect.die("A writer-locked conversation must not start a fresh thread"), + raw: { request: () => Effect.fail(refusal) }, + }; + + const error = yield* openCodexThread({ + client, + threadId: ThreadId.make("thread-1"), + runtimeMode: "full-access", + cwd: "/tmp/project", + requestedModel: "gpt-5.3-codex", + serviceTier: undefined, + resumeThreadId: "01a0ccd7-23c1-7c00-9cbd-eafe286d36b5", + }).pipe(Effect.flip); + + NodeAssert.ok(isCodexAppServerRequestError(error)); + NodeAssert.ok(error.errorMessage.includes("01a0ccd7-23c1-7c00-9cbd-eafe286d36b5")); + NodeAssert.match(error.errorMessage, /writer lock/i); + NodeAssert.match(error.errorMessage, /close/i); + NodeAssert.equal(error.cause, refusal); + }), + ); }); diff --git a/apps/server/src/provider/Layers/CodexSessionRuntime.ts b/apps/server/src/provider/Layers/CodexSessionRuntime.ts index 81ca7b2cb6..ade2386346 100644 --- a/apps/server/src/provider/Layers/CodexSessionRuntime.ts +++ b/apps/server/src/provider/Layers/CodexSessionRuntime.ts @@ -78,6 +78,11 @@ const RECOVERABLE_THREAD_RESUME_ERROR_SNIPPETS = [ "no rollout found", ]; +// The app-server refuses a resume while another Codex host holds the conversation's +// writer lock. Resuming is the only correct outcome, so this must not fall back to a +// fresh thread: that would strand the conversation's history behind a new one. +const THREAD_WRITER_LOCKED_ERROR_SNIPPET = "already has an active writer"; + export function hasConfiguredMcpServer(appServerArgs: ReadonlyArray | undefined): boolean { return appServerArgs?.some((argument) => argument.includes("mcp_servers.")) === true; } @@ -717,6 +722,19 @@ function classifyCodexStderrLine(rawLine: string): { readonly message: string } return { message: line }; } +export function isThreadWriterLockedError(error: unknown): boolean { + const message = (error instanceof Error ? error.message : String(error)).toLowerCase(); + return message.includes(THREAD_WRITER_LOCKED_ERROR_SNIPPET); +} + +function describeThreadWriterLock(resumeThreadId: string): string { + return ( + `Codex conversation ${resumeThreadId} is open in another Codex app, which holds its writer lock. ` + + "Close that conversation there, most often in the Codex desktop app, then send again. " + + "This thread keeps its history instead of continuing in a new conversation." + ); +} + export function isRecoverableThreadResumeError(error: unknown): boolean { const message = (error instanceof Error ? error.message : String(error)).toLowerCase(); if (!message.includes("thread")) { @@ -801,6 +819,15 @@ export const openCodexThread = (input: { ), ), ), + Effect.mapError((error) => + isThreadWriterLockedError(error) + ? CodexErrors.CodexAppServerRequestError.internalError( + describeThreadWriterLock(resumeThreadId), + undefined, + { method: "thread/resume", operation: "receive-response", cause: error }, + ) + : error, + ), Effect.catchIf( (error) => !input.strictResume && isRecoverableThreadResumeError(error), (error) => diff --git a/docs/user/triage.md b/docs/user/triage.md index 3ad5ccdb60..093eda33b7 100644 --- a/docs/user/triage.md +++ b/docs/user/triage.md @@ -69,3 +69,12 @@ can retry the message after checking that the selected provider is signed in and If a late provider response arrives after that timeout, Pylon ignores it. The late response cannot revive the failed turn. If retries keep timing out, run the triage command above and include the provider name and the time of the failed attempt. + +## A Codex thread says it already has an active writer + +Codex allows one writer per conversation across every app on your machine. When the same +conversation is already open somewhere else, most often the Codex desktop app, Pylon cannot resume +it and the turn fails with a message naming that conversation. + +Close the conversation in the other app and send your message again. Pylon does not silently start a +fresh conversation here, so the thread keeps its history. From 63a7805d5d6153984c97b0ff6b57a0d96e1d5bd8 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:11:53 -0600 Subject: [PATCH 079/104] chore(mobile): check indexed access and overrides (#822) --- .../src/NativeMarkdownSelectableText.tsx | 2 +- .../scripts/generate-uniwind-themes.mts | 6 ++-- .../src/components/AndroidAnchoredMenu.tsx | 2 +- .../review/nativeReviewDiffAdapter.ts | 17 +++++++---- .../src/features/review/reviewModel.test.ts | 4 +-- .../src/features/terminal/terminalTheme.ts | 30 +++++++++++++++++-- .../src/lib/mobileTheme.test-support.ts | 2 +- apps/mobile/src/lib/mobileTheme.test.ts | 12 ++++---- apps/mobile/src/lib/mobileTheme.ts | 29 +++++++++--------- apps/mobile/src/lib/wideMarkdownBlocks.ts | 6 +++- apps/mobile/tsconfig.json | 4 ++- 11 files changed, 76 insertions(+), 38 deletions(-) diff --git a/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx b/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx index ed8c14a03d..531125fa18 100644 --- a/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx +++ b/apps/mobile/modules/t3-markdown-text/src/NativeMarkdownSelectableText.tsx @@ -98,7 +98,7 @@ function resolveHeadingFontSize(textStyle: NativeMarkdownTextStyle, headingLevel } const scale = textStyle.fontSize / DEFAULT_BODY_FONT_SIZE; - return Math.max(12, Math.round(DEFAULT_HEADING_FONT_SIZES[index] * scale)); + return Math.max(12, Math.round((DEFAULT_HEADING_FONT_SIZES[index] ?? 15) * scale)); } function runStyle(run: NativeMarkdownTextRun, textStyle: NativeMarkdownTextStyle): TextStyle { diff --git a/apps/mobile/scripts/generate-uniwind-themes.mts b/apps/mobile/scripts/generate-uniwind-themes.mts index ec6da30651..e1faae4bbe 100644 --- a/apps/mobile/scripts/generate-uniwind-themes.mts +++ b/apps/mobile/scripts/generate-uniwind-themes.mts @@ -53,7 +53,7 @@ const color = (family: TailwindColorFamily, shade?: TailwindColorShade, opacity // These replace the remaining dark:* utility pairs. A registered palette theme is // neither literally `light` nor `dark`, so appearance-sensitive values must also be // represented as semantic variables for custom themes. -const ADAPTIVE_COLORS = { +const ADAPTIVE_COLORS: Readonly> = { "--color-adaptive-amber-100-500-a18": [color("amber", 100), color("amber", 500, 0.18)], "--color-adaptive-amber-50-950-a40": [color("amber", 50), color("amber", 950, 0.4)], "--color-adaptive-amber-200-900-a60": [color("amber", 200), color("amber", 900, 0.6)], @@ -155,9 +155,9 @@ export const customThemeNames = BUILT_IN_THEME_IDS.flatMap((themeId) => const adaptiveVariablesFor = (appearance: MobileThemeAppearance) => Object.fromEntries( - Object.entries(ADAPTIVE_COLORS).map(([name, values]) => [ + Object.entries(ADAPTIVE_COLORS).map(([name, [light, dark]]) => [ name, - values[appearance === "light" ? 0 : 1], + appearance === "light" ? light : dark, ]), ); diff --git a/apps/mobile/src/components/AndroidAnchoredMenu.tsx b/apps/mobile/src/components/AndroidAnchoredMenu.tsx index 1a4f11b8c7..79ec95d0a0 100644 --- a/apps/mobile/src/components/AndroidAnchoredMenu.tsx +++ b/apps/mobile/src/components/AndroidAnchoredMenu.tsx @@ -125,7 +125,7 @@ export function AndroidAnchoredMenu(props: AndroidAnchoredMenuProps) { return () => subscription.remove(); }, [anchor, close, submenuDepth]); - const parent = path.length > 0 ? path[path.length - 1] : null; + const parent = path[path.length - 1] ?? null; const levelActions = (parent?.subactions ?? props.actions).filter( (action) => !(action.attributes?.hidden ?? false), ); diff --git a/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts b/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts index ed01fa8048..d89563570e 100644 --- a/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts +++ b/apps/mobile/src/features/review/nativeReviewDiffAdapter.ts @@ -51,11 +51,15 @@ function opaqueNativeHexColor(color: string, background: string): string { if (!rgba || !backgroundHex) return background; const alpha = rgba[4] === undefined ? 1 : Math.min(1, Math.max(0, Number(rgba[4]))); - const channels = [1, 2, 3].map((index) => { - const foreground = Number(rgba[index]); - const behind = Number.parseInt(backgroundHex[index], 16); - return Math.round(foreground * alpha + behind * (1 - alpha)); - }); + const channels: number[] = []; + for (const index of [1, 2, 3]) { + const foregroundChannel = rgba[index]; + const backgroundChannel = backgroundHex[index]; + if (foregroundChannel === undefined || backgroundChannel === undefined) return background; + const foreground = Number(foregroundChannel); + const behind = Number.parseInt(backgroundChannel, 16); + channels.push(Math.round(foreground * alpha + behind * (1 - alpha))); + } return `#${channels.map((channel) => channel.toString(16).padStart(2, "0")).join("")}`; } @@ -367,6 +371,9 @@ function addNativeWordDiffRanges( for (let pairIndex = 0; pairIndex < pairedCount; pairIndex += 1) { const deletedRowIndex = deletedRowIndexes[pairIndex]; const addedRowIndex = addedRowIndexes[pairIndex]; + if (deletedRowIndex === undefined || addedRowIndex === undefined) { + continue; + } const deletedRow = nextRows[deletedRowIndex]; const addedRow = nextRows[addedRowIndex]; if (!deletedRow?.content || !addedRow?.content) { diff --git a/apps/mobile/src/features/review/reviewModel.test.ts b/apps/mobile/src/features/review/reviewModel.test.ts index 6a85108945..aadddfc9f0 100644 --- a/apps/mobile/src/features/review/reviewModel.test.ts +++ b/apps/mobile/src/features/review/reviewModel.test.ts @@ -85,7 +85,7 @@ describe("buildReviewSectionItems", () => { }, ]; - const loadedTurnId = getReviewSectionIdForCheckpoint(checkpoints[0]); + const loadedTurnId = getReviewSectionIdForCheckpoint(checkpoints[0]!); const items = buildReviewSectionItems({ checkpoints, gitSections, @@ -93,7 +93,7 @@ describe("buildReviewSectionItems", () => { [loadedTurnId]: "diff --git a/loaded.ts b/loaded.ts", }, loadingTurnIds: { - [getReviewSectionIdForCheckpoint(checkpoints[1])]: true, + [getReviewSectionIdForCheckpoint(checkpoints[1]!)]: true, }, loadingGitSections: false, }); diff --git a/apps/mobile/src/features/terminal/terminalTheme.ts b/apps/mobile/src/features/terminal/terminalTheme.ts index 64a54e8ea3..f7d96b6686 100644 --- a/apps/mobile/src/features/terminal/terminalTheme.ts +++ b/apps/mobile/src/features/terminal/terminalTheme.ts @@ -1,4 +1,8 @@ -import { BUILT_IN_THEMES, getThemeColorsForAppearance } from "@t3tools/shared/themePalettes"; +import { + BUILT_IN_THEMES, + T3_CHAT_THEME, + getThemeColorsForAppearance, +} from "@t3tools/shared/themePalettes"; import { getMobileThemeVariables, @@ -15,9 +19,29 @@ export interface TerminalTheme { readonly border: string; readonly cursorForeground: string; readonly cursorBackground: string; - readonly palette: readonly string[]; + /** The 16 ANSI colors, in order. A fixed tuple so indexed reads are never undefined. */ + readonly palette: TerminalPalette; } +type TerminalPalette = readonly [ + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, + string, +]; + const PIERRE_LIGHT_THEME: TerminalTheme = { // Pierre terminal palette with the app's shared screen background. background: "#f2f2f7", @@ -85,7 +109,7 @@ export function getMobileTerminalTheme( const base = getPierreTerminalTheme(scheme); if (themeId === "t3-code" || themeId === "material-you") return base; - const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? BUILT_IN_THEMES[0]; + const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; const palette = getThemeColorsForAppearance(theme, scheme) ?? theme.colors; const colors = getMobileThemeVariables(themeId, scheme); const background = themeColorToNativeColor(palette.terminalBackground); diff --git a/apps/mobile/src/lib/mobileTheme.test-support.ts b/apps/mobile/src/lib/mobileTheme.test-support.ts index a702bb9afb..1e47b0f0ed 100644 --- a/apps/mobile/src/lib/mobileTheme.test-support.ts +++ b/apps/mobile/src/lib/mobileTheme.test-support.ts @@ -14,7 +14,7 @@ export function readDefaultMobileThemeVariables( return Object.fromEntries( Array.from(variant.matchAll(/(--color-[a-z0-9-]+):\s*([^;]+);/gu), ([, name, value]) => [ name, - value.trim(), + (value ?? "").trim(), ]), ) as MobileThemeVariables; } diff --git a/apps/mobile/src/lib/mobileTheme.test.ts b/apps/mobile/src/lib/mobileTheme.test.ts index bf7df9afe4..bcd8f589ec 100644 --- a/apps/mobile/src/lib/mobileTheme.test.ts +++ b/apps/mobile/src/lib/mobileTheme.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vite-plus/test"; -import { BUILT_IN_THEME_IDS, BUILT_IN_THEMES } from "@t3tools/shared/themePalettes"; +import { BUILT_IN_THEME_IDS, BUILT_IN_THEMES, T3_CHAT_THEME } from "@t3tools/shared/themePalettes"; import { readDefaultMobileThemeVariables } from "./mobileTheme.test-support"; import { @@ -49,7 +49,7 @@ function compositeOver(overlay: string, background: string): string { describe("mobile themes", () => { it("declares every runtime theme variable in the static stylesheet", () => { - const generatedVariables = createMobileThemeVariables(BUILT_IN_THEMES[0].colors, "light"); + const generatedVariables = createMobileThemeVariables(T3_CHAT_THEME.colors, "light"); expect(Object.keys(readDefaultMobileThemeVariables("light")).sort()).toEqual( Object.keys(generatedVariables).sort(), ); @@ -152,16 +152,16 @@ describe("mobile themes", () => { }); it("maps semantic palette roles onto every mobile color variable", () => { - const variables = createMobileThemeVariables(BUILT_IN_THEMES[0].colors, "light"); + const variables = createMobileThemeVariables(T3_CHAT_THEME.colors, "light"); expect(Object.keys(variables)).toHaveLength(75); expect(variables["--color-sheet-solid"]).toBe( - themeColorToNativeColor(BUILT_IN_THEMES[0].colors.chrome), + themeColorToNativeColor(T3_CHAT_THEME.colors.chrome), ); expect(variables["--color-warning"]).toBe( - themeColorToNativeColor(BUILT_IN_THEMES[0].colors.warningSurface), + themeColorToNativeColor(T3_CHAT_THEME.colors.warningSurface), ); expect(variables["--color-warning-foreground"]).toBe( - themeColorToNativeColor(BUILT_IN_THEMES[0].colors.warningForeground), + themeColorToNativeColor(T3_CHAT_THEME.colors.warningForeground), ); expect(variables["--color-primary"]).not.toBe(variables["--color-screen"]); expect(variables["--color-primary-shadow"]).toBe("#000000"); diff --git a/apps/mobile/src/lib/mobileTheme.ts b/apps/mobile/src/lib/mobileTheme.ts index 79698c2f52..4226f66087 100644 --- a/apps/mobile/src/lib/mobileTheme.ts +++ b/apps/mobile/src/lib/mobileTheme.ts @@ -1,5 +1,6 @@ import { BUILT_IN_THEMES, + T3_CHAT_THEME, getThemeColorsForAppearance, MOBILE_DEFAULT_THEME_ID, MOBILE_THEME_IDS as SHARED_MOBILE_THEME_IDS, @@ -29,7 +30,9 @@ export const MOBILE_THEME_OPTIONS: ReadonlyArray<{ ...BUILT_IN_THEMES.map((theme) => ({ id: theme.id as MobileThemeId, label: theme.label })), ]; -export type MobileThemeVariable = `--color-${string}`; +// Closed set: every key `createMobileThemeVariables` writes. Reads of a +// misspelled variable then fail to compile instead of yielding undefined. +export type MobileThemeVariable = keyof ReturnType; export type MobileThemeVariables = Readonly>; export function normalizeMobileThemeId(value: unknown): MobileThemeId { @@ -136,9 +139,10 @@ function withAlpha(color: string, alpha: number): string { function rgbChannels(color: string): readonly [number, number, number] | null { const match = /^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(color); - return match - ? [Number.parseInt(match[1], 16), Number.parseInt(match[2], 16), Number.parseInt(match[3], 16)] - : null; + if (!match) return null; + const [, red, green, blue] = match; + if (red === undefined || green === undefined || blue === undefined) return null; + return [Number.parseInt(red, 16), Number.parseInt(green, 16), Number.parseInt(blue, 16)]; } /** @@ -216,18 +220,15 @@ function readableForeground(foreground: string, surface: string, minimumContrast } export function themeColorWithAlpha(color: string, alpha: number): string { - const hex = /^#([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(color); - if (hex) { - return `rgba(${Number.parseInt(hex[1], 16)}, ${Number.parseInt(hex[2], 16)}, ${Number.parseInt(hex[3], 16)}, ${alpha})`; + const channels = rgbChannels(color); + if (channels) { + return `rgba(${channels[0]}, ${channels[1]}, ${channels[2]}, ${alpha})`; } const rgb = /^rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/.exec(color); return rgb ? `rgba(${rgb[1]}, ${rgb[2]}, ${rgb[3]}, ${alpha})` : color; } -export function createMobileThemeVariables( - colors: ThemeColors, - appearance: MobileThemeAppearance, -): MobileThemeVariables { +export function createMobileThemeVariables(colors: ThemeColors, appearance: MobileThemeAppearance) { const c = nativeColors(colors); return { "--color-screen": c.canvas, @@ -312,7 +313,7 @@ export function createMobileThemeVariables( } export const MOBILE_THEME_VARIABLE_NAMES = Object.keys( - createMobileThemeVariables(BUILT_IN_THEMES[0].colors, "light"), + createMobileThemeVariables(T3_CHAT_THEME.colors, "light"), ) as ReadonlyArray; export function getMobileThemeVariables( @@ -320,7 +321,7 @@ export function getMobileThemeVariables( appearance: MobileThemeAppearance, overrides: Partial | null = null, ): MobileThemeVariables { - const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? BUILT_IN_THEMES[0]; + const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; const colors = getThemeColorsForAppearance(theme, appearance) ?? theme.colors; const baseVariables = createMobileThemeVariables(colors, appearance); @@ -334,7 +335,7 @@ export function getMobileThemePreviewColors( ): ThemePreviewColors { if (themeId === DEFAULT_MOBILE_THEME_ID || themeId === "material-you") return STANDARD_THEME_PREVIEW_COLORS[appearance]; - const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? BUILT_IN_THEMES[0]; + const theme = BUILT_IN_THEMES.find((candidate) => candidate.id === themeId) ?? T3_CHAT_THEME; const colors = getThemeColorsForAppearance(theme, appearance) ?? theme.colors; return { canvas: themeColorToNativeColor(colors.canvas), diff --git a/apps/mobile/src/lib/wideMarkdownBlocks.ts b/apps/mobile/src/lib/wideMarkdownBlocks.ts index 57588bab2a..054c60be08 100644 --- a/apps/mobile/src/lib/wideMarkdownBlocks.ts +++ b/apps/mobile/src/lib/wideMarkdownBlocks.ts @@ -81,7 +81,11 @@ function hasOrderedListItem(text: string): boolean { const nestedMatch = INDENTED_ORDERED_LIST_ITEM.exec(line); const parentMatch = previousNonEmptyLine === null ? null : ANY_LIST_ITEM.exec(previousNonEmptyLine); - if (nestedMatch && parentMatch && parentMatch[1].length < nestedMatch[1].length) { + if ( + nestedMatch?.[1] !== undefined && + parentMatch?.[1] !== undefined && + parentMatch[1].length < nestedMatch[1].length + ) { return true; } diff --git a/apps/mobile/tsconfig.json b/apps/mobile/tsconfig.json index f3763c5654..f6634fbc4c 100644 --- a/apps/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -2,6 +2,8 @@ "extends": "expo/tsconfig.base", "compilerOptions": { "allowImportingTsExtensions": true, - "strict": true + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true } } From 36af785c692babde396a79da837c1aac3e429fdc Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:14:10 -0600 Subject: [PATCH 080/104] fix(web): keep PR panel handoffs in the current thread (#826) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- apps/web/src/components/ChatView.tsx | 51 ++- .../PullRequestDetailPanel.test.tsx | 342 ++++++++++++++++++ .../pullRequest/PullRequestDetailPanel.tsx | 8 +- .../pullRequestDetail.logic.test.ts | 112 ++++-- .../pullRequest/pullRequestDetail.logic.ts | 99 ++++- 5 files changed, 538 insertions(+), 74 deletions(-) create mode 100644 apps/web/src/components/pullRequest/PullRequestDetailPanel.test.tsx diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 189970ef35..b389eb9192 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -222,7 +222,10 @@ import { selectThreadPreviewMiniPlayer, usePreviewMiniPlayerStore, } from "../previewMiniPlayerStore"; -import { isThreadOwnPullRequest } from "./pullRequest/pullRequestDetail.logic"; +import { + pullRequestPanelContext, + pullRequestPanelHost, +} from "./pullRequest/pullRequestDetail.logic"; import { PullRequestDetailPanel } from "./pullRequest/PullRequestDetailPanel"; import { PullRequestDetailGhost } from "./pullRequest/PullRequestGhosts"; import { PullRequestsUnavailableState } from "./pullRequest/PullRequestsUnavailableState"; @@ -4998,6 +5001,8 @@ export default function ChatView(props: ChatViewProps) { ) : linkedThreadPullRequestKey; const activeProjectRepository = activeProject?.repositoryIdentity?.displayName ?? null; + const activeProjectHost = + activeProject?.repositoryIdentity?.canonicalKey.split("/")[0]?.trim().toLowerCase() || null; const threadRepository = linkedThreadPullRequest?.repository ?? activeProjectRepository; const openThreadPullRequest = useCallback( (number: number) => { @@ -5007,18 +5012,28 @@ export default function ChatView(props: ChatViewProps) { const projectId = linkedThreadPullRequest?.projectId ?? activeProject?.id; const repository = linkedThreadPullRequest?.repository ?? activeProjectRepository; if (projectId === undefined || repository === null) return; + const host = pullRequestPanelHost({ + links: visiblePullRequests, + repository, + number, + linkedUrl: linkedThreadPullRequest?.url, + projectHost: activeProjectHost, + }); useRightPanelStore.getState().openPullRequest(activeThreadRef, { projectId, + ...(host ? { host } : {}), repository, number, }); }, [ activeProject, + activeProjectHost, activeProjectRepository, activeThreadRef, linkedThreadPullRequest, supportsPullRequests, + visiblePullRequests, ], ); const openProjectPullRequest = useCallback( @@ -5033,11 +5048,18 @@ export default function ChatView(props: ChatViewProps) { } useRightPanelStore.getState().openPullRequest(activeThreadRef, { projectId: activeProject.id, + ...(activeProjectHost ? { host: activeProjectHost } : {}), repository: activeProjectRepository, number, }); }, - [activeProject, activeProjectRepository, activeThreadRef, supportsPullRequests], + [ + activeProject, + activeProjectHost, + activeProjectRepository, + activeThreadRef, + supportsPullRequests, + ], ); const proactivePanelObservationRef = useRef ({ + newThread: vi.fn(), + prepareThread: vi.fn(), + refresh: vi.fn(), + Wrapper: ({ children }: { children?: ReactNode }) => children, + Trigger: ({ children, render }: { children?: ReactNode; render?: ReactElement }) => ( + <> + {render} + {children} + + ), +})); +vi.mock("@effect/atom-react", () => ({ useAtomValue: () => [] })); +vi.mock("~/state/server", () => ({ + primaryServerKeybindingsAtom: {}, + serverEnvironment: { configValueAtom: () => null }, +})); +vi.mock("~/state/entities", () => ({ useProjects: () => [], useServerConfigs: () => new Map() })); +vi.mock("~/state/environments", () => ({ + useEnvironments: () => ({ environments: [] }), + usePrimaryEnvironmentId: () => EnvironmentId.make("env-1"), +})); +vi.mock("~/hooks/useSettings", () => ({ + useClientSettings: (select: (settings: typeof DEFAULT_CLIENT_SETTINGS) => unknown) => + select(DEFAULT_CLIENT_SETTINGS), +})); +vi.mock("~/hooks/useLiveRefresh", () => ({ useLiveRefresh: () => {} })); +vi.mock("~/hooks/useHandleNewThread", () => ({ useNewThreadHandler: () => newThread })); +vi.mock("~/lib/sourceControlActions", () => ({ + usePreparePullRequestThreadAction: () => ({ run: prepareThread }), +})); +vi.mock("~/state/use-atom-command", () => ({ useAtomCommand: () => vi.fn() })); +vi.mock("~/state/pullRequests", async (importOriginal) => ({ + ...(await importOriginal()), + pullRequestEnvironment: { detail: () => "detail", activity: () => "activity" }, + newestPullRequestSummary: (first: unknown, second: unknown) => first ?? second, + usePullRequestTurnRefresh: () => 0, + useSharedPullRequestSummary: () => null, +})); +vi.mock("~/state/vcs", () => ({ vcsEnvironment: { listRefs: () => null } })); +vi.mock("~/state/query", () => ({ + useEnvironmentQuery: (query: string) => ({ + data: query === "detail" ? detail : null, + isPending: false, + isSuccess: true, + error: null, + refresh, + }), +})); +vi.mock("~/state/usePullRequestStack", () => ({ + usePullRequestStack: () => ({ + data: null, + isSuccess: true, + isPending: false, + error: null, + refresh, + }), +})); +vi.mock("../ui/toast", () => ({ toastManager: { add: vi.fn(), update: vi.fn() } })); +vi.mock("../ui/tooltip", () => ({ + TooltipProvider: Wrapper, + Tooltip: Wrapper, + TooltipTrigger: Trigger, + TooltipPopup: () => null, +})); +vi.mock("../ui/menu", () => ({ + Menu: Wrapper, + MenuPopup: Wrapper, + MenuTrigger: Trigger, + MenuItem: "button", + MenuRadioGroup: Wrapper, + MenuRadioItem: "button", + MenuSeparator: () => null, + MenuShortcut: () => null, +})); +vi.mock("../ui/alert-dialog", () => ({ + AlertDialog: () => null, + AlertDialogPopup: Wrapper, + AlertDialogHeader: Wrapper, + AlertDialogTitle: Wrapper, + AlertDialogDescription: Wrapper, + AlertDialogFooter: Wrapper, + AlertDialogClose: Wrapper, +})); +vi.mock("./PullRequestMarkdown", () => ({ + PullRequestMarkdownContext: Wrapper, + PullRequestMarkdown: () => null, +})); +vi.mock("~/browser/useOpenLink", () => ({ useOpenLink: () => vi.fn() })); +vi.mock("./PullRequestThreadLinks", () => ({ PullRequestThreadLinks: () => null })); +vi.mock("./PullRequestSummaryTab", () => ({ + PullRequestSummaryTab: ({ + onFixFinding, + }: ComponentProps) => ( + + ), +})); +vi.mock("./PullRequestCodeTab", () => ({ + default: ({ + onAddToAgentSelection, + }: ComponentProps) => ( + + ), +})); + +import { PullRequestDetailPanel } from "./PullRequestDetailPanel"; +import { pullRequestPanelContext } from "./pullRequestDetail.logic"; + +const detail: PullRequestDetailView = { + provider: "github", + projectId: ProjectId.make("project"), + projectTitle: "Project", + workspaceRoot: "/workspace", + repository: "owner/repo", + number: 1, + title: "Test pull request", + body: "Original description", + url: "https://github.com/owner/repo/pull/1", + author: { login: "author", name: null, avatarUrl: null }, + viewer: "author", + state: "open", + isDraft: false, + mergeability: "conflicting", + additions: 1, + deletions: 0, + changedFiles: 1, + headBranch: "feature", + baseBranch: "main", + createdAt: "2026-09-01T00:00:00Z", + updatedAt: "2026-09-01T00:00:00Z", + mergedAt: null, + closedAt: null, + reviewers: [], + labels: [], + checks: [{ name: "Unit tests", status: "success", description: null, url: null }], + comments: [], + commentCount: 0, + commentsTruncated: false, + reviewThreads: [], + commits: [], + mergeCapabilities: { merge: false, squash: false, rebase: false }, + capabilities: { + diff: true, + comment: false, + search: true, + actions: [], + mergeMethods: [], + review: { inlineComment: false, reply: false, resolve: false, verdicts: [] }, + reviewers: { request: false, listCandidates: false }, + edit: { changeRequest: true, comment: false }, + }, + viewerPermissions: { + actions: [], + comment: false, + resolve: false, + verdicts: [], + requestReviewers: false, + }, +}; + +const threadRef: ScopedThreadRef = { + environmentId: EnvironmentId.make("env-1"), + threadId: ThreadId.make("thread-1"), +}; +const draftId = DraftId.make("draft-1"); +const newDraftId = DraftId.make("new-draft"); +let renderer: ReactTestRenderer; + +beforeEach(() => { + vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); + vi.stubGlobal("window", { addEventListener: vi.fn(), removeEventListener: vi.fn() }); + useComposerDraftStore.setState({ draftsByThreadKey: {} }); + newThread + .mockReset() + .mockResolvedValue({ draftId: newDraftId, threadId: ThreadId.make("new-thread") }); + prepareThread.mockReset().mockResolvedValue({ + _tag: "Success", + value: { branch: "feature", worktreePath: "/workspace/pr" }, + }); +}); +afterEach(() => { + act(() => renderer?.unmount()); + vi.unstubAllGlobals(); +}); + +async function click(label: string) { + const button = renderer.root + .findAllByType("button") + .find( + (node) => + node.props["aria-label"] === label || + node.findAll((child) => child.children.includes(label)).length > 0, + ); + expect(button, label).toBeDefined(); + await act(async () => + button!.props.onClick({ + nativeEvent: new Event("click"), + preventDefault() {}, + stopPropagation() {}, + }), + ); +} + +const actions = [ + "Resolve conflicts", + "Ask a question", + "Explain this PR", + "Fix findings in this thread", + "Fix check", + "Add to agent", +]; + +// The surface ChatView opens for `detail`, and the thread states it can be opened beside. The +// context prop is derived here the way ChatView derives it, so a wrong answer from the thread's +// link list fails these cases rather than only a hand-picked prop. +const surface = { projectId: detail.projectId, repository: detail.repository, number: 1 }; +const link = (number: number, source: ThreadPullRequestLink["source"]): ThreadPullRequestLink => ({ + host: "github.com", + repository: detail.repository, + number, + url: `https://github.com/${detail.repository}/pull/${number}`, + source, + linkedAt: "2026-09-01T00:00:00Z", + snapshot: null, + stack: null, +}); +const stackThread = { + projectId: detail.projectId, + pullRequests: [link(3, "manual"), link(2, "stack"), link(1, "stack")], + // The server's one-slot field names the top layer; the panel shows the bottom one. + linkedPullRequest: { ...surface, number: 3, url: link(3, "manual").url }, +}; +const unrelatedThread = { + projectId: detail.projectId, + pullRequests: [link(9, "created")], + linkedPullRequest: { ...surface, number: 9, url: link(9, "created").url }, +}; + +describe.each([ + ["own PR, a lower layer of the thread's stack", stackThread, threadRef], + ["another PR beside the current thread", unrelatedThread, threadRef], + ["PR beside an unsent draft", null, draftId], + ["standalone PR page", null, undefined], +] as const)("%s", (_name, thread, target) => { + const context = thread ? pullRequestPanelContext(thread, surface) : "page"; + + function render() { + renderer = create( + ({ + isWeb: true, + isDesktop: false, + terminalFocus: false, + terminalOpen: false, + previewFocus: false, + previewOpen: false, + })} + />, + ); + } + + it(`${thread === stackThread ? "hides" : "offers"} the checkout`, async () => { + await act(async () => render()); + const checkout = renderer.root + .findAllByType("button") + .filter((node) => node.props["aria-label"] === "Check out"); + expect(checkout).toHaveLength(thread === stackThread ? 0 : 1); + }); + + it.each(actions)("%s writes to the correct composer", async (action) => { + if (target) useComposerDraftStore.getState().setPrompt(target, "Keep my draft"); + await act(async () => render()); + if (action === "Add to agent") await click("Code"); + await click(target ? action : action.replace("in this thread", "in a thread")); + const draft = useComposerDraftStore.getState().getComposerDraft(target ?? newDraftId); + if (action === "Resolve conflicts") expect(draft?.prompt).toContain("resolve every conflict"); + else if (action === "Fix check") expect(draft?.prompt).toContain("Fix the failing check"); + else if (action.startsWith("Fix findings")) + expect(draft?.prompt).toContain("Fix the actionable findings"); + else expect(draft?.reviewComments?.length).toBeGreaterThan(0); + if (action === "Add to agent") { + expect(draft?.prompt).toContain("Fix this line"); + expect(draft?.reviewComments).toEqual( + expect.arrayContaining([expect.objectContaining({ id: "note-1", diff: "+broken()" })]), + ); + } + if (target) { + expect(draft?.prompt).toContain("Keep my draft"); + expect(newThread).not.toHaveBeenCalled(); + expect(prepareThread).not.toHaveBeenCalled(); + } else { + expect(newThread).toHaveBeenCalled(); + } + }); +}); diff --git a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx index 3eaea42e8f..3353ced339 100644 --- a/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx +++ b/apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx @@ -152,7 +152,6 @@ import { isStackedPullRequestBase, pullRequestActionMenuHasGroup, pullRequestActionNeedsHostRefresh, - pullRequestComposerTarget, panelPullRequestCheckoutCommand, pullRequestFindingKey, pullRequestHandoffLabels, @@ -1096,10 +1095,9 @@ export function PullRequestDetailPanel({ reviewComments?: ReadonlyArray; }; - // Beside the thread whose own pull request this is, a task belongs in that thread's composer: - // the branch is already checked out under it, so opening a second thread would only scatter - // the work. - const attachTarget = pullRequestComposerTarget(context, composerDraftTarget); + // A detail panel beside an active composer keeps hand-offs in that thread even when the + // inspected pull request is not its checkout. `context` still controls checkout-only actions. + const attachTarget = composerDraftTarget ?? null; const handoffLabels = pullRequestHandoffLabels(attachTarget !== null); const writeTaskToComposer = (target: ScopedThreadRef | DraftId, task: ThreadTask) => { diff --git a/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts b/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts index 9c0a93ea0d..9239d7560c 100644 --- a/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts +++ b/apps/web/src/components/pullRequest/pullRequestDetail.logic.test.ts @@ -10,6 +10,7 @@ import { type PullRequestRef, type PullRequestReviewThread, type RepositoryIdentity, + type ThreadPullRequestLink, } from "@t3tools/contracts"; import { describe, expect, it, vi } from "vite-plus/test"; import { formatInlineContextReference } from "~/lib/composerContextReferences"; @@ -30,7 +31,8 @@ import { isStackedPullRequestBase, loadingPullRequestCheckoutCommand, panelPullRequestCheckoutCommand, - isThreadOwnPullRequest, + pullRequestPanelContext, + pullRequestPanelHost, latestPullRequestReviewOutcomes, newestPullRequestCommitAt, mergePullRequestThreadComments, @@ -38,7 +40,6 @@ import { pullRequestActionMenuHasGroup, pullRequestActionNeedsHostRefresh, pullRequestCheckoutCommand, - pullRequestComposerTarget, pullRequestFindingKey, pullRequestHandoffLabels, pullRequestReviewOutcome, @@ -333,15 +334,6 @@ describe("pull request handoff labels", () => { }); }); -describe("pull request composer target", () => { - it("rejects a page composer so agent comments cannot open another thread", () => { - const target = { environmentId: "env-1", threadId: "thread-1" }; - - expect(pullRequestComposerTarget("page", target)).toBeNull(); - expect(pullRequestComposerTarget("thread", target)).toBe(target); - }); -}); - describe("stacked pull request classification", () => { it("requires a known default branch", () => { expect(isStackedPullRequestBase("main", [{ name: "main", isDefault: false }])).toBe(false); @@ -1466,39 +1458,97 @@ describe("how the branch stands against its base", () => { }); describe("whether the panel is showing the thread's own pull request", () => { - const surface = { projectId: "proj-a", repository: "acme/app", number: 7 }; - - it("matches on project, repository and number together", () => { + const surface = { projectId: "proj-a", host: "github.com", repository: "acme/app", number: 7 }; + const link = (overrides: Partial = {}): ThreadPullRequestLink => ({ + host: "github.com", + repository: "acme/app", + number: 7, + url: "https://github.com/acme/app/pull/7", + source: "manual", + linkedAt: "2026-09-24T00:00:00.000Z", + snapshot: null, + stack: null, + ...overrides, + }); + + it("recognizes every visible linked PR, including a lower stack layer", () => { expect( - isThreadOwnPullRequest({ projectId: "proj-a", repository: "acme/app", number: 7 }, surface), - ).toBe(true); + pullRequestPanelContext( + { projectId: "proj-a", pullRequests: [link({ number: 8 }), link()] }, + surface, + ), + ).toBe("thread"); }); - it("rejects a second checkout of the same repository under another project", () => { + it("rejects tombstones, another host, and another project for checkout context", () => { expect( - isThreadOwnPullRequest({ projectId: "proj-b", repository: "acme/app", number: 7 }, surface), - ).toBe(false); + pullRequestPanelContext( + { + projectId: "proj-a", + pullRequests: [link({ source: "stack-dismissed" })], + linkedPullRequest: { repository: "acme/app", number: 7 }, + }, + surface, + ), + ).toBe("page"); + expect( + pullRequestPanelContext( + { projectId: "proj-a", pullRequests: [link({ host: "forge.example" })] }, + surface, + ), + ).toBe("page"); + expect(pullRequestPanelContext({ projectId: "proj-b", pullRequests: [link()] }, surface)).toBe( + "page", + ); }); - it("rejects another repository or another number", () => { + it("does not confuse identical repository and number on two hosts", () => { + const github = link(); + const forgejo = link({ + host: "forge.example", + url: "https://forge.example/acme/app/pulls/7", + }); + const thread = { projectId: "proj-a", pullRequests: [github, forgejo] }; expect( - isThreadOwnPullRequest({ projectId: "proj-a", repository: "acme/web", number: 7 }, surface), - ).toBe(false); + pullRequestPanelContext(thread, { + projectId: surface.projectId, + repository: surface.repository, + number: surface.number, + }), + ).toBe("page"); + expect(pullRequestPanelContext(thread, { ...surface, host: "forge.example" })).toBe("thread"); expect( - isThreadOwnPullRequest({ projectId: "proj-a", repository: "acme/app", number: 8 }, surface), - ).toBe(false); + pullRequestPanelHost({ + links: thread.pullRequests, + repository: surface.repository, + number: 7, + linkedUrl: forgejo.url, + projectHost: "github.com", + }), + ).toBe("forge.example"); + expect( + pullRequestPanelHost({ + links: thread.pullRequests, + repository: surface.repository, + number: 7, + }), + ).toBeNull(); }); - it("rejects a thread with no project or no pull request of its own", () => { - expect( - isThreadOwnPullRequest({ projectId: null, repository: "acme/app", number: 7 }, surface), - ).toBe(false); + it("uses legacy slots when a server has no link list", () => { expect( - isThreadOwnPullRequest( - { projectId: "proj-a", repository: "acme/app", number: null }, + pullRequestPanelContext( + { + projectId: "proj-a", + linkedPullRequest: { repository: "acme/app", number: 8 }, + branchPullRequest: { repository: "acme/app", number: 7 }, + }, surface, ), - ).toBe(false); + ).toBe("thread"); + expect(pullRequestPanelContext({ projectId: "proj-a", linkedPullRequest: null }, surface)).toBe( + "page", + ); }); }); diff --git a/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts b/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts index 70b161c9c7..e8ed50fb29 100644 --- a/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts +++ b/apps/web/src/components/pullRequest/pullRequestDetail.logic.ts @@ -21,8 +21,14 @@ import { type PullRequestState, type PullRequestUpdateMethod, type SourceControlProviderKind, + type ThreadLinkedPullRequest, + type ThreadPullRequestLink, type VcsRef, } from "@t3tools/contracts"; +import { + threadPullRequestKeysEqual, + visibleThreadPullRequests, +} from "@t3tools/shared/threadPullRequests"; import { inferReviewCommentFenceLanguage, type ReviewCommentContext } from "~/reviewCommentContext"; import { reviewCommentContextId } from "~/lib/composerContextRecords"; @@ -191,28 +197,88 @@ export function editPullRequestThreadComment< return comments.map((comment) => (comment.id === commentId ? { ...comment, body } : comment)); } -/** - * Whether the pull request on a right-panel surface is the thread's own one. Repository and - * number are not enough: one environment can hold two checkouts of the same repository under - * different projects, and the other project's checkout is somebody else's branch. - */ -export function isThreadOwnPullRequest( +/** Whether this surface is one of the current thread's visible links. Keep checkout actions + * tied to project and host identity; the composer handoff is decided separately. */ +export function pullRequestPanelContext( thread: { readonly projectId: string | null; - readonly repository: string | null; - readonly number: number | null; + readonly pullRequests?: ReadonlyArray | undefined; + readonly linkedPullRequest?: + | Pick + | null + | undefined; + readonly branchPullRequest?: + | Pick + | null + | undefined; }, surface: { readonly projectId: string; + readonly host?: string; readonly repository: string; readonly number: number; }, -): boolean { - return ( - thread.projectId === surface.projectId && - thread.repository === surface.repository && - thread.number === surface.number +): "thread" | "page" { + if (thread.projectId !== surface.projectId) return "page"; + const links = visibleThreadPullRequests(thread.pullRequests ?? []); + if (links.length > 0) { + const host = surface.host; + // A hostless legacy surface cannot identify one of two otherwise identical host links. + const match = + host === undefined + ? new Set( + links + .filter( + (link) => + link.repository.toLowerCase() === surface.repository.toLowerCase() && + link.number === surface.number, + ) + .map((link) => link.host.toLowerCase()), + ).size === 1 + : links.some((link) => + threadPullRequestKeysEqual(link, { + host, + repository: surface.repository, + number: surface.number, + }), + ); + return match ? "thread" : "page"; + } + if ((thread.pullRequests?.length ?? 0) > 0) return "page"; + // Old servers expose one or both legacy slots without the full host-aware link list. + return [thread.linkedPullRequest, thread.branchPullRequest].some( + (link) => + link !== null && + link !== undefined && + link.repository === surface.repository && + link.number === surface.number, + ) + ? "thread" + : "page"; +} + +/** Prefer the exact linked URL, then the checkout's remote host. A number shared by two hosts + * is ambiguous without either signal, so leave that surface hostless and let context fail safe. */ +export function pullRequestPanelHost(input: { + readonly links: ReadonlyArray; + readonly repository: string; + readonly number: number; + readonly linkedUrl?: string | undefined; + readonly projectHost?: string | null | undefined; +}): string | null { + const matches = visibleThreadPullRequests(input.links).filter( + (link) => + link.number === input.number && + link.repository.toLowerCase() === input.repository.toLowerCase(), ); + const selected = + matches.find((link) => link.url === input.linkedUrl) ?? + matches.find((link) => link.host.toLowerCase() === input.projectHost?.toLowerCase()); + if (selected) return selected.host; + if (new Set(matches.map((link) => link.host.toLowerCase())).size === 1) { + return matches[0]?.host ?? null; + } + return input.projectHost ?? null; } /** Names where a pull-request task will land, without letting each surface guess independently. */ @@ -230,13 +296,6 @@ export function pullRequestHandoffLabels(inThisThread: boolean) { }; } -export function pullRequestComposerTarget( - context: "page" | "thread", - target: T | null | undefined, -): T | null { - return context === "thread" ? (target ?? null) : null; -} - /** Whether the open pull-request action group contains at least one action. */ export function pullRequestActionMenuHasGroup( showsDraftToggle: boolean, From 36ce2f8520ae155756ce2bd3dbfc0f97d84fb135 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:15:31 -0600 Subject: [PATCH 081/104] feat(web): filter sidebar from thread menu (#821) --- apps/web/src/components/Sidebar.tsx | 37 ++++++++++++++----- .../components/threadActionMenu.logic.test.ts | 22 +++++++++++ .../src/components/threadActionMenu.logic.ts | 21 ++++++++++- apps/web/src/hooks/useThreadActionMenu.ts | 1 + 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index d2974a48d5..20e0faac6b 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -4508,10 +4508,24 @@ export default function Sidebar() { const isPinned = thread.pinnedAt != null; // Presets resolve at menu-open time (same as the popover). const snoozePresets = resolveSnoozePresets(new Date(), timestampFormat); + const threadProjectGroup = + projectGroupsRef.current.find((project) => + project.memberProjectRefs.some( + (projectRef) => + projectRef.environmentId === thread.environmentId && + projectRef.projectId === thread.projectId, + ), + ) ?? null; const clicked = await settlePromise(() => api.contextMenu.show( buildThreadActionMenuItems({ branch: thread.branch ?? null, + projectFilter: threadProjectGroup + ? { + label: threadProjectGroup.displayName, + isActive: projectScopeKey === threadProjectGroup.projectKey, + } + : null, isPinned, isSettled, isSnoozed, @@ -4539,17 +4553,18 @@ export default function Sidebar() { return; } switch (clicked.value) { - case "project-settings": { - const projectGroup = projectGroupsRef.current.find((group) => - group.memberProjectRefs.some( - (projectRef) => - projectRef.environmentId === thread.environmentId && - projectRef.projectId === thread.projectId, - ), - ); - if (projectGroup) openProjectSettings(projectGroup); + case "filter-by-project": + if (threadProjectGroup) { + setProjectScopeKey( + projectScopeKey === threadProjectGroup.projectKey + ? null + : threadProjectGroup.projectKey, + ); + } + return; + case "project-settings": + if (threadProjectGroup) openProjectSettings(threadProjectGroup); return; - } case "new-thread-on-branch": { // Explicit branch carry-over: reuse the thread's worktree when it // has one, otherwise its branch on the local checkout. @@ -4710,8 +4725,10 @@ export default function Sidebar() { handleMultiSelectContextMenu, markThreadUnread, openProjectSettings, + projectScopeKey, projectByKey, serverConfigs, + setProjectScopeKey, startThreadRename, updateThreadMetadata, timestampFormat, diff --git a/apps/web/src/components/threadActionMenu.logic.test.ts b/apps/web/src/components/threadActionMenu.logic.test.ts index 1bdd046937..7b56e17c29 100644 --- a/apps/web/src/components/threadActionMenu.logic.test.ts +++ b/apps/web/src/components/threadActionMenu.logic.test.ts @@ -4,6 +4,7 @@ import { buildThreadActionMenuItems, type ThreadActionMenuState } from "./thread const baseState: ThreadActionMenuState = { branch: null, + projectFilter: null, isPinned: false, isSettled: false, isSnoozed: false, @@ -47,6 +48,27 @@ describe("buildThreadActionMenuItems", () => { expect(items[copyIndex + 2]?.id).toBe("archive"); }); + it("offers project filtering only for the sidebar's scoped thread list", () => { + expect(ids(baseState)).not.toContain("filter-by-project"); + expect( + buildThreadActionMenuItems({ + ...baseState, + projectFilter: { label: "Beta Project", isActive: false }, + }).find((item) => item.id === "filter-by-project"), + ).toMatchObject({ label: "Filter by Beta Project", icon: "folder-tree" }); + }); + + it("offers a way back to all projects once the sidebar is scoped", () => { + const items = buildThreadActionMenuItems({ + ...baseState, + projectFilter: { label: "Beta Project", isActive: true }, + }); + const filterIndex = items.findIndex((item) => item.id === "filter-by-project"); + expect(items[filterIndex]).toMatchObject({ label: "Show all projects", icon: "folder-tree" }); + expect(items[filterIndex - 1]?.id).toBe("mark-unread"); + expect(items[filterIndex + 1]?.id).toBe("copy"); + }); + it("includes branch items only for threads with a branch", () => { const withBranch = allIds({ ...baseState, branch: "feat/menu" }); expect(withBranch).toContain("new-thread-on-branch"); diff --git a/apps/web/src/components/threadActionMenu.logic.ts b/apps/web/src/components/threadActionMenu.logic.ts index 5ba266f770..519c2d5491 100644 --- a/apps/web/src/components/threadActionMenu.logic.ts +++ b/apps/web/src/components/threadActionMenu.logic.ts @@ -8,6 +8,7 @@ import type { SnoozePreset } from "@t3tools/client-runtime/state/thread-settled" */ export type ThreadActionMenuId = | "new-thread-on-branch" + | "filter-by-project" | "project-settings" | "pin" | "unpin" @@ -28,6 +29,11 @@ export type ThreadActionMenuId = export interface ThreadActionMenuState { readonly branch: string | null; + /** Null on menus without a project-scoped thread list, such as the chat header. */ + readonly projectFilter: { + readonly label: string; + readonly isActive: boolean; + } | null; readonly isPinned: boolean; readonly isSettled: boolean; readonly isSnoozed: boolean; @@ -46,8 +52,8 @@ export interface ThreadActionMenuState { /** * Single source for the per-thread action menu: the sidebar row's right-click - * menu and the chat header menu both render exactly this list, so labels, - * ordering, and capability gating cannot drift between the two surfaces. + * menu and the chat header menu share labels, ordering, and capability gating. + * Each surface supplies state for the actions it supports. */ export function buildThreadActionMenuItems( state: ThreadActionMenuState, @@ -107,6 +113,17 @@ export function buildThreadActionMenuItems( ] : []), { id: "mark-unread", label: "Mark unread", icon: "mail-open" }, + ...(state.projectFilter + ? [ + { + id: "filter-by-project" as const, + label: state.projectFilter.isActive + ? "Show all projects" + : `Filter by ${state.projectFilter.label}`, + icon: "folder-tree", + }, + ] + : []), { id: "copy", label: "Copy", diff --git a/apps/web/src/hooks/useThreadActionMenu.ts b/apps/web/src/hooks/useThreadActionMenu.ts index a66ea21b98..ca3bc20642 100644 --- a/apps/web/src/hooks/useThreadActionMenu.ts +++ b/apps/web/src/hooks/useThreadActionMenu.ts @@ -139,6 +139,7 @@ export function useThreadActionMenu(input: { const snoozePresets = resolveSnoozePresets(now, timestampFormat); const items = buildThreadActionMenuItems({ branch: thread.branch ?? null, + projectFilter: null, isPinned: thread.pinnedAt != null, isSettled: supports.settlement && thread.settledOverride === "settled", isSnoozed: supports.snooze && effectiveSnoozed(thread, { now: now.toISOString() }), From ea7fa449485124e03002ebb85ff71454888e0c93 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:22:39 -0600 Subject: [PATCH 082/104] feat(web): add bounded route history shortcuts on desktop (#803) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- apps/web/src/components/AppSidebarLayout.tsx | 84 ++++- .../src/navigationHistoryShortcuts.test.ts | 355 ++++++++++++++++++ apps/web/src/navigationHistoryShortcuts.ts | 94 +++++ docs/user/keybindings.md | 11 +- packages/contracts/src/keybindings.ts | 2 + packages/shared/src/keybindings.ts | 10 + 6 files changed, 553 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/navigationHistoryShortcuts.test.ts create mode 100644 apps/web/src/navigationHistoryShortcuts.ts diff --git a/apps/web/src/components/AppSidebarLayout.tsx b/apps/web/src/components/AppSidebarLayout.tsx index 9b199ec872..7ffdf842d1 100644 --- a/apps/web/src/components/AppSidebarLayout.tsx +++ b/apps/web/src/components/AppSidebarLayout.tsx @@ -7,16 +7,28 @@ import { useAtomValue } from "@effect/atom-react"; import * as Schema from "effect/Schema"; import { useEffect, + useRef, useState, useSyncExternalStore, type CSSProperties, type ReactNode, } from "react"; -import { useLocation, useNavigate } from "@tanstack/react-router"; +import { useLocation, useNavigate, useParams, useRouter } from "@tanstack/react-router"; import { isElectron } from "../env"; import { getLocalStorageItem, removeLocalStorageItem } from "../hooks/useLocalStorage"; import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings"; +import { isPreviewFocused } from "../lib/previewFocus"; +import { isTerminalFocused } from "../lib/terminalFocus"; +import { isModelPickerOpen } from "../modelPickerVisibility"; +import { + handleNavigationHistoryShortcut, + observeNavigationHistory, + type ObservedNavigationHistory, +} from "../navigationHistoryShortcuts"; +import { selectActiveRightPanel, useRightPanelStore } from "../rightPanelStore"; +import { selectThreadTerminalUiState, useTerminalUiStateStore } from "../terminalUiStateStore"; +import { resolveThreadRouteRef } from "../threadRoutes"; import { cn, isMacPlatform } from "../lib/utils"; import { primaryServerKeybindingsAtom } from "../state/server"; import { @@ -137,6 +149,75 @@ function SidebarControl() { ); } +function NavigationHistoryShortcuts() { + const keybindings = useAtomValue(primaryServerKeybindingsAtom); + const router = useRouter(); + const historyEntry = router.history.location; + const observedHistory = useRef({ + keys: new Map([ + [historyEntry.state.__TSR_index, historyEntry.state.__TSR_key ?? historyEntry.href], + ]), + minIndex: historyEntry.state.__TSR_index, + maxIndex: historyEntry.state.__TSR_index, + }); + const routeThreadRef = useParams({ + strict: false, + select: (params) => resolveThreadRouteRef(params), + }); + + useEffect(() => { + return router.history.subscribe(({ location, action }) => { + observedHistory.current = observeNavigationHistory( + observedHistory.current, + location.state.__TSR_index, + location.state.__TSR_key ?? location.href, + action.type, + ); + }); + }, [router]); + + useEffect(() => { + const onKeyDown = (event: KeyboardEvent) => { + handleNavigationHistoryShortcut( + event, + keybindings, + { + terminalFocus: isTerminalFocused(), + terminalOpen: routeThreadRef + ? selectThreadTerminalUiState( + useTerminalUiStateStore.getState().terminalUiStateByThreadKey, + routeThreadRef, + ).terminalOpen + : false, + previewFocus: isPreviewFocused(), + previewOpen: routeThreadRef + ? selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, routeThreadRef) === + "preview" + : false, + editableFocus: + event.target instanceof HTMLElement && + event.target.closest( + 'input, textarea, select, [contenteditable]:not([contenteditable="false"])', + ) !== null, + modelPickerOpen: isModelPickerOpen(), + isDesktop: isElectron, + isWeb: !isElectron, + }, + router.history.location.state.__TSR_index, + observedHistory.current.minIndex, + observedHistory.current.maxIndex, + router.history, + isElectron, + ); + }; + + window.addEventListener("keydown", onKeyDown); + return () => window.removeEventListener("keydown", onKeyDown); + }, [keybindings, routeThreadRef, router]); + + return null; +} + // Settings swaps the thread sidebar out of the tree. Keep the lightweight // project projection subscribed so returning to a draft never renders the // zero-project state while the environment snapshot reconnects. @@ -271,6 +352,7 @@ export function AppSidebarLayout({ children }: { children: ReactNode }) { {children} + ); diff --git a/apps/web/src/navigationHistoryShortcuts.test.ts b/apps/web/src/navigationHistoryShortcuts.test.ts new file mode 100644 index 0000000000..c92389ccd7 --- /dev/null +++ b/apps/web/src/navigationHistoryShortcuts.test.ts @@ -0,0 +1,355 @@ +import { + compileResolvedKeybindingsConfig, + DEFAULT_RESOLVED_KEYBINDINGS, +} from "@t3tools/shared/keybindings"; +import { createMemoryHistory } from "@tanstack/react-router"; +import { describe, expect, it } from "vite-plus/test"; + +import { + handleNavigationHistoryShortcut, + isProtectedNavigationShortcutTarget, + observeNavigationHistory, +} from "./navigationHistoryShortcuts"; +import type { ShortcutMatchContext } from "./keybindings"; + +const context: ShortcutMatchContext = { + terminalFocus: false, + terminalOpen: false, + previewFocus: false, + previewOpen: false, + editableFocus: false, + modelPickerOpen: false, + isDesktop: true, + isWeb: false, +}; + +function keyEvent(key: string, code: string, overrides: Record = {}) { + let prevented = false; + let stopped = false; + return { + event: { + key, + code, + metaKey: true, + ctrlKey: false, + altKey: false, + shiftKey: false, + defaultPrevented: false, + isComposing: false, + repeat: false, + target: null, + preventDefault: () => { + prevented = true; + }, + stopPropagation: () => { + stopped = true; + }, + ...overrides, + } as unknown as KeyboardEvent, + get prevented() { + return prevented; + }, + get stopped() { + return stopped; + }, + }; +} + +function navigationHistory() { + let back = 0; + let forward = 0; + return { + history: { + back: () => { + back += 1; + }, + forward: () => { + forward += 1; + }, + }, + get back() { + return back; + }, + get forward() { + return forward; + }, + }; +} + +describe("navigation history shortcuts", () => { + it("keeps native browser brackets and handles a custom nonnative browser binding", () => { + const history = navigationHistory(); + const native = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + native.event, + DEFAULT_RESOLVED_KEYBINDINGS, + { ...context, isDesktop: false, isWeb: true }, + 2, + 1, + 2, + history.history, + false, + "MacIntel", + ), + ).toBe(false); + expect(native.prevented).toBe(false); + + const custom = compileResolvedKeybindingsConfig([ + { key: "alt+arrowleft", command: "navigation.back" }, + ]); + const customEvent = keyEvent("ArrowLeft", "ArrowLeft", { metaKey: false, altKey: true }); + expect( + handleNavigationHistoryShortcut( + customEvent.event, + custom, + { ...context, isDesktop: false, isWeb: true }, + 2, + 1, + 2, + history.history, + false, + "MacIntel", + ), + ).toBe(true); + expect(history.back).toBe(1); + }); + + it("truncates an observed forward branch when a new route replaces its index", () => { + const initial = { keys: new Map([[4, "a"]]), minIndex: 4, maxIndex: 4 }; + const atFive = observeNavigationHistory(initial, 5, "b", "PUSH"); + const atSix = observeNavigationHistory(atFive, 6, "c", "PUSH"); + expect(atSix.maxIndex).toBe(6); + const returned = observeNavigationHistory(atSix, 5, "b", "BACK"); + const replaced = observeNavigationHistory(returned, 5, "new-b", "REPLACE"); + expect(replaced.maxIndex).toBe(6); + expect(replaced.keys.has(6)).toBe(true); + const pushed = observeNavigationHistory(returned, 5, "new-b", "PUSH"); + expect(pushed.maxIndex).toBe(5); + expect(pushed.minIndex).toBe(4); + expect(pushed.keys.has(6)).toBe(false); + }); + + it("does not send Back before a nonzero first Pylon history index", () => { + const history = navigationHistory(); + const first = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + first.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 4, + 4, + 4, + history.history, + true, + "MacIntel", + ), + ).toBe(true); + expect(first.prevented).toBe(true); + expect(history.back).toBe(0); + }); + + it("navigates nested route history and leaves the first and last route in place", () => { + const history = createMemoryHistory({ + initialEntries: ["/", "/settings/general", "/settings/keybindings"], + initialIndex: 2, + }); + const back = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + back.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 2, + 0, + 2, + history, + true, + "MacIntel", + ), + ).toBe(true); + expect(history.location.href).toBe("/settings/general"); + + const forward = keyEvent("]", "BracketRight"); + handleNavigationHistoryShortcut( + forward.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 1, + 0, + 2, + history, + true, + "MacIntel", + ); + expect(history.location.href).toBe("/settings/keybindings"); + handleNavigationHistoryShortcut( + forward.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 2, + 0, + 2, + history, + true, + "MacIntel", + ); + expect(history.location.href).toBe("/settings/keybindings"); + }); + + it("moves between nested routes and consumes back at the app boundary", () => { + const history = navigationHistory(); + const back = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + back.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 2, + 0, + 2, + history.history, + true, + "MacIntel", + ), + ).toBe(true); + expect(history.back).toBe(1); + expect(back.prevented).toBe(true); + expect(back.stopped).toBe(true); + + const boundary = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + boundary.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 0, + 0, + 2, + history.history, + true, + "MacIntel", + ), + ).toBe(true); + expect(history.back).toBe(1); + expect(boundary.prevented).toBe(true); + + const forward = keyEvent("]", "BracketRight"); + expect( + handleNavigationHistoryShortcut( + forward.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 0, + 0, + 2, + history.history, + true, + "MacIntel", + ), + ).toBe(true); + expect(history.forward).toBe(1); + const noAppForward = keyEvent("]", "BracketRight"); + expect( + handleNavigationHistoryShortcut( + noAppForward.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 0, + 0, + 0, + history.history, + true, + "MacIntel", + ), + ).toBe(true); + expect(history.forward).toBe(1); + expect(noAppForward.prevented).toBe(true); + }); + + it("leaves focused terminal, preview, editor, and captured controls alone", () => { + const history = navigationHistory(); + for (const focus of [ + "terminalFocus", + "previewFocus", + "editableFocus", + "modelPickerOpen", + ] as const) { + const input = keyEvent("[", "BracketLeft"); + expect( + handleNavigationHistoryShortcut( + input.event, + DEFAULT_RESOLVED_KEYBINDINGS, + { ...context, [focus]: true }, + 1, + 0, + 1, + history.history, + true, + "MacIntel", + ), + ).toBe(false); + expect(input.prevented).toBe(false); + } + expect(isProtectedNavigationShortcutTarget({ closest: () => ({}) as Element })).toBe(true); + expect(isProtectedNavigationShortcutTarget({ closest: () => null })).toBe(false); + expect(history.back).toBe(0); + }); + + it("does not take repeated or already handled keyboard events", () => { + const history = navigationHistory(); + for (const overrides of [{ repeat: true }, { defaultPrevented: true }, { isComposing: true }]) { + const input = keyEvent("[", "BracketLeft", overrides); + expect( + handleNavigationHistoryShortcut( + input.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 1, + 0, + 1, + history.history, + true, + "MacIntel", + ), + ).toBe(false); + expect(input.prevented).toBe(false); + } + expect(history.back).toBe(0); + }); + + it("uses Control on Linux and leaves unrelated keys for other commands", () => { + const history = navigationHistory(); + const linuxForward = keyEvent("]", "BracketRight", { metaKey: false, ctrlKey: true }); + expect( + handleNavigationHistoryShortcut( + linuxForward.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 1, + 0, + 2, + history.history, + true, + "Linux", + ), + ).toBe(true); + expect(history.forward).toBe(1); + const threadJump = keyEvent("1", "Digit1", { metaKey: false, ctrlKey: true }); + expect( + handleNavigationHistoryShortcut( + threadJump.event, + DEFAULT_RESOLVED_KEYBINDINGS, + context, + 1, + 0, + 2, + history.history, + true, + "Linux", + ), + ).toBe(false); + expect(threadJump.prevented).toBe(false); + }); +}); diff --git a/apps/web/src/navigationHistoryShortcuts.ts b/apps/web/src/navigationHistoryShortcuts.ts new file mode 100644 index 0000000000..572376dfad --- /dev/null +++ b/apps/web/src/navigationHistoryShortcuts.ts @@ -0,0 +1,94 @@ +import type { ResolvedKeybindingsConfig } from "@t3tools/contracts"; + +import { resolveShortcutCommand, type ShortcutMatchContext } from "./keybindings"; +import { isMacPlatform } from "./lib/utils"; + +type NavigationHistory = Pick; +type NavigationHistoryAction = "PUSH" | "REPLACE" | "BACK" | "FORWARD" | "GO"; + +export interface ObservedNavigationHistory { + readonly keys: ReadonlyMap; + readonly minIndex: number; + readonly maxIndex: number; +} + +export function observeNavigationHistory( + previous: ObservedNavigationHistory, + index: number, + key: string, + action: NavigationHistoryAction, +): ObservedNavigationHistory { + if (previous.keys.get(index) === key && action !== "PUSH") return previous; + const keys = new Map(previous.keys); + // PUSH truncates the forward branch; REPLACE preserves it. + if (action === "PUSH") { + for (const knownIndex of keys.keys()) { + if (knownIndex > index) keys.delete(knownIndex); + } + } + keys.set(index, key); + return { keys, minIndex: previous.minIndex, maxIndex: Math.max(...keys.keys()) }; +} + +export function isProtectedNavigationShortcutTarget( + target: { closest: (selector: string) => Element | null } | null, +): boolean { + return ( + target?.closest( + 'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [data-keybinding-capture]', + ) !== null && target !== null + ); +} + +export function handleNavigationHistoryShortcut( + event: KeyboardEvent, + keybindings: ResolvedKeybindingsConfig, + context: ShortcutMatchContext, + historyIndex: number, + minObservedHistoryIndex: number, + maxObservedHistoryIndex: number, + history: NavigationHistory, + isDesktop: boolean, + platform?: string, +): boolean { + if (event.defaultPrevented || event.isComposing || event.repeat) return false; + if (context.modelPickerOpen) return false; + if ( + typeof Element !== "undefined" && + event.target instanceof Element && + isProtectedNavigationShortcutTarget(event.target) + ) { + return false; + } + + const command = resolveShortcutCommand(event, keybindings, { + context, + ...(platform ? { platform } : {}), + }); + if (command !== "navigation.back" && command !== "navigation.forward") return false; + + const resolvedPlatform = platform ?? navigator.platform; + const nativeModifier = isMacPlatform(resolvedPlatform) + ? event.metaKey && !event.ctrlKey + : event.ctrlKey && !event.metaKey; + const nativeBracket = + event.code === "BracketLeft" || + event.code === "BracketRight" || + event.key === "[" || + event.key === "]"; + if (!isDesktop && nativeModifier && nativeBracket && !event.altKey && !event.shiftKey) { + return false; + } + + event.preventDefault(); + event.stopPropagation(); + if (command === "navigation.back") { + // The first observed entry may have a nonzero browser index. + if (historyIndex > minObservedHistoryIndex) history.back(); + } else { + // A global forward entry may belong to a different site. Only follow a + // route index Pylon has actually observed in this mounted session. + if (historyIndex < maxObservedHistoryIndex) history.forward(); + } + return true; +} diff --git a/docs/user/keybindings.md b/docs/user/keybindings.md index 94d2cd54ad..4ce1e83b16 100644 --- a/docs/user/keybindings.md +++ b/docs/user/keybindings.md @@ -64,8 +64,9 @@ and Control elsewhere. Other modifiers are `cmd` / `meta`, `ctrl` / `control`, ` ## When conditions Available context keys are `terminalFocus`, `terminalOpen`, `previewFocus`, `previewOpen`, -`modelPickerOpen`, `isWeb`, and `isDesktop`. `isWeb` is true in a browser tab; `isDesktop` is true in -the desktop app. Unknown keys evaluate to `false`. +`editableFocus`, `modelPickerOpen`, `isWeb`, and `isDesktop`. `isWeb` is true in a browser tab; +`isDesktop` is true in the desktop app. `editableFocus` is supplied for route-history shortcuts +when an input or editable element has focus. Unknown keys evaluate to `false`. The default `mod+1` through `mod+9` thread and model jumps run only in the desktop app so a browser tab keeps its own numbered-tab shortcuts. Remove the `isDesktop` condition in Settings if @@ -90,6 +91,12 @@ chooser. Both use your [new-thread defaults](./thread-sidebar.md#start-a-thread) `filePicker.toggle` (`mod+p`) opens file search for the active project, and `projectSearch.toggle` (`mod+shift+f`) searches inside its files. Repeating either shortcut closes that search. +`navigation.back` (`mod+[`) and `navigation.forward` (`mod+]`) move through Pylon's route history in +the desktop app. Back stops at the first route observed in the current app session; Forward only +uses routes observed in that session. Browser tabs keep their native bracket history shortcuts; +other custom bindings for these commands can navigate Pylon's observed routes. Both defaults leave +focused editors, terminal input, and preview controls alone. + `thread.copyReference` (`mod+shift+c`) copies the open pull request panel URL, then the thread's pull request link, or its thread ID when no pull request is available. `thread.settle` (`mod+shift+s`) settles the active thread or restores it, and `thread.pin` (`mod+shift+p`) pins or unpins it. diff --git a/packages/contracts/src/keybindings.ts b/packages/contracts/src/keybindings.ts index a285d2fcf4..00cc378bd2 100644 --- a/packages/contracts/src/keybindings.ts +++ b/packages/contracts/src/keybindings.ts @@ -55,6 +55,8 @@ export type ModelPickerKeybindingCommand = (typeof MODEL_PICKER_KEYBINDING_COMMA export const STATIC_KEYBINDING_COMMANDS = [ "sidebar.toggle", + "navigation.back", + "navigation.forward", "terminal.toggle", "terminal.split", "terminal.splitVertical", diff --git a/packages/shared/src/keybindings.ts b/packages/shared/src/keybindings.ts index 718f33d39f..3d7eac0a37 100644 --- a/packages/shared/src/keybindings.ts +++ b/packages/shared/src/keybindings.ts @@ -20,6 +20,16 @@ type WhenToken = export const DEFAULT_KEYBINDINGS: ReadonlyArray = [ { key: "mod+b", command: "sidebar.toggle" }, + { + key: "mod+[", + command: "navigation.back", + when: "isDesktop && !terminalFocus && !editableFocus && !previewFocus && !modelPickerOpen", + }, + { + key: "mod+]", + command: "navigation.forward", + when: "isDesktop && !terminalFocus && !editableFocus && !previewFocus && !modelPickerOpen", + }, { key: "mod+j", command: "terminal.toggle" }, { key: "mod+alt+b", command: "rightPanel.toggle" }, { key: "mod+d", command: "terminal.split", when: "terminalFocus" }, From 70b4b3b4c0437a507a853f489d1afd54c0eb518d Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:24:17 -0600 Subject: [PATCH 083/104] fix(web): add local dropped folders as path chips (#818) --- apps/desktop/src/preload.ts | 3 +- apps/web/src/components/ChatView.tsx | 2 + ...omposerPromptEditor.serialization.test.tsx | 11 ++ apps/web/src/components/LegacySidebar.tsx | 5 + apps/web/src/components/Sidebar.tsx | 10 ++ apps/web/src/components/chat/ChatComposer.tsx | 67 +++++++- .../src/components/chat/folderDrop.test.ts | 113 +++++++++++++ apps/web/src/components/chat/folderDrop.ts | 35 ++++ .../components/chat/workspaceFileDrop.test.ts | 154 +++++++++++++++++- .../src/components/chat/workspaceFileDrop.ts | 67 +++++++- packages/contracts/src/ipc.ts | 2 + 11 files changed, 464 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/components/chat/folderDrop.test.ts create mode 100644 apps/web/src/components/chat/folderDrop.ts diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index c99481ffef..d9228cd04f 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -8,7 +8,7 @@ import type { DesktopSnapShotEvent, } from "@t3tools/contracts"; import { exposeClerkBridge } from "@clerk/electron/preload"; -import { contextBridge, ipcRenderer } from "electron"; +import { contextBridge, ipcRenderer, webUtils } from "electron"; import * as IpcChannels from "./ipc/channels.ts"; @@ -72,6 +72,7 @@ contextBridge.exposeInMainWorld("desktopBridge", { } return result as ReturnType; }, + getPathForFile: (file: File) => webUtils.getPathForFile(file), // oxlint-disable-next-line t3code/no-global-process-runtime -- The sandboxed Electron preload exposes the client platform, not the server platform. getClientPlatform: () => process.platform, setNotificationBadge: (badge) => diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index b389eb9192..666a362baa 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -10479,6 +10479,8 @@ export default function ChatView(props: ChatViewProps) { const workspaceFileDropHandlers = makeWorkspaceFileDropHandlers({ setDragActive: setIsWorkspaceFileDragActive, addFiles: (files) => composerRef.current?.addDroppedFiles(files), + addFolders: (folders, unresolvedCount) => + composerRef.current?.addDroppedFolders(folders, unresolvedCount), }); const externalComposerDrawerAttached = composerBannerItems.length > 0 || Boolean(threadSyncPhase && !activeEnvironmentUnavailable); diff --git a/apps/web/src/components/ComposerPromptEditor.serialization.test.tsx b/apps/web/src/components/ComposerPromptEditor.serialization.test.tsx index 98d72e246c..263c837568 100644 --- a/apps/web/src/components/ComposerPromptEditor.serialization.test.tsx +++ b/apps/web/src/components/ComposerPromptEditor.serialization.test.tsx @@ -3,6 +3,7 @@ import { $copyNode, $getRoot, $isElementNode, PASTE_COMMAND, type LexicalEditor import { act, createRef } from "react"; import { create, type ReactTestRenderer } from "react-test-renderer"; import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; +import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; import { collapseExpandedComposerCursor } from "../composer-logic"; import { ComposerPromptEditor, type ComposerPromptEditorHandle } from "./ComposerPromptEditor"; @@ -84,6 +85,16 @@ afterEach(async () => { }); describe("composer mention serialization", () => { + it("turns an absolute dropped folder path into a retained path chip", async () => { + const path = "/tmp/My Project/contracts"; + const prompt = `${serializeComposerFileLink(path)} `; + await renderPrompt(prompt); + expect(editorRef.current?.readSnapshot().value).toBe(prompt); + expect(lexicalEditor.getEditorState().read(() => $firstMention().exportJSON())).toMatchObject({ + path, + }); + }); + it.each([ "@README.md control", "@terminal-1:3 Explain this output\n\n\n- Terminal 1 line 3:\n 3 | output\n", diff --git a/apps/web/src/components/LegacySidebar.tsx b/apps/web/src/components/LegacySidebar.tsx index 85f30356d3..74f87943ac 100644 --- a/apps/web/src/components/LegacySidebar.tsx +++ b/apps/web/src/components/LegacySidebar.tsx @@ -401,6 +401,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP addFiles: (files) => { onFileDropThreads(threadRef, files); }, + addFolders: () => + toastManager.add({ + type: "error", + title: "Drop folders into an open local thread composer", + }), }), [onFileDropThreads, threadRef], ); diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 20e0faac6b..3672400bc5 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1418,6 +1418,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { addFiles: (files) => { onFileDropThreads(threadRef, files); }, + addFolders: () => + toastManager.add({ + type: "error", + title: "Drop folders into an open local thread composer", + }), }) : null, [onFileDropThreads, threadRef], @@ -2473,6 +2478,11 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { addFiles: (files) => { props.onFileDropThreads(threadRef, files); }, + addFolders: () => + toastManager.add({ + type: "error", + title: "Drop folders into an open local thread composer", + }), }), [props.onFileDropThreads, threadRef], ); diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 7d60f8a2a9..3e094c2ad8 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -5,6 +5,8 @@ import { environmentThreadDetails } from "../../state/threads"; import { scopeThreadRef } from "@t3tools/client-runtime/environment"; import { trackPendingContextImport } from "./pendingContextImport"; import { observeResponsiveBreakpointFade, usePanelAnimationSettings } from "../../panelAnimations"; +import { usePrimaryEnvironmentId } from "../../state/environments"; +import { readPrimaryEnvironmentTarget } from "../../environments/primary/target"; import { runtimeModeConfig, runtimeModeOptions } from "./runtimeModeConfig"; import { useRightPanelStore } from "~/rightPanelStore"; import { AttachmentFilePreview } from "../files/AttachmentFilePreview"; @@ -47,6 +49,7 @@ import { resolveServerProviderRuntimeMode, ProviderDriverKind, ProviderInstanceId, + PRIMARY_LOCAL_ENVIRONMENT_ID, PROVIDER_SEND_TURN_MAX_ATTACHMENTS, PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, } from "@t3tools/contracts"; @@ -88,6 +91,7 @@ import { supportsSessionResourceReload, } from "@t3tools/client-runtime/state/session-resources"; import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; +import { folderDropTarget, resolveDroppedFolderPath } from "./folderDrop"; import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model"; import { memo, @@ -1383,6 +1387,7 @@ export interface ChatComposerHandle { restoreAfterTimelineReachedEnd: () => void; collapseForTimelineScrollKey: (key: string) => void; addDroppedFiles: (files: File[]) => void; + addDroppedFolders: (folders: File[], unresolvedCount: number) => void; hasPendingAttachments: () => boolean; insertTextAtEnd: ( text: string, @@ -1751,7 +1756,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) onFileOpen, } = props; const [isQuickQuestionOpen, setIsQuickQuestionOpen] = useState(false); - + const primaryEnvironmentId = usePrimaryEnvironmentId(); const activeTasksProgress = props.threadSyncPhase === null ? props.activeTasksProgress : null; const activeTaskSteps = props.threadSyncPhase === null ? props.activeTaskSteps : null; // ------------------------------------------------------------------ @@ -6706,6 +6711,64 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) if (!inserted) focusComposer(); }); }, + addDroppedFolders: (folders: File[], unresolvedCount: number) => { + let desktopManagedPrimary = false; + let primaryRunningDistro: string | null | undefined; + try { + desktopManagedPrimary = readPrimaryEnvironmentTarget().source === "desktop-managed"; + primaryRunningDistro = window.desktopBridge + ?.getLocalEnvironmentBootstraps() + .find((entry) => entry.id === PRIMARY_LOCAL_ENVIRONMENT_ID)?.runningDistro; + } catch { + // An unavailable local bootstrap must not make a client path look server-local. + } + const target = folderDropTarget({ + desktopManagedPrimary, + primaryRunningDistro, + hasNativePathBridge: window.desktopBridge?.getPathForFile !== undefined, + environmentId, + primaryEnvironmentId, + }); + if (target !== "local") { + toastManager.add({ + type: "error", + title: + target === "remote" + ? "Folders can't be dropped into remote environments" + : "Folder paths are available only in the desktop local environment", + }); + return; + } + if (unresolvedCount > 0) { + toastManager.add({ + type: "error", + title: "Some dropped items could not be read", + description: "Drop the items again or type folder paths with @ instead.", + }); + } + for (const folder of folders) { + const path = resolveDroppedFolderPath(folder, window.desktopBridge?.getPathForFile); + if (path === null) { + toastManager.add({ + type: "error", + title: `Couldn't get the path of "${folder.name}"`, + description: "Type the folder path with @ instead.", + }); + continue; + } + const inserted = insertComposerTextAtEnd(`${serializeComposerFileLink(path)} `, { + ensureLeadingBoundary: true, + }); + if (!inserted) { + toastManager.add({ + type: "error", + title: `Couldn't add "${folder.name}" to the composer`, + description: "The composer is busy; try again when it is ready.", + }); + } + } + focusComposer(); + }, hasPendingAttachments: () => (pendingImageCompressionsRef.current.get(attachmentTargetKey) ?? 0) > 0 || pendingDraftWork.has(attachmentTargetKey), @@ -6862,6 +6925,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) composerPreviewAnnotations, composerReviewComments, focusComposer, + environmentId, + primaryEnvironmentId, isConnecting, isComposerApprovalState, pendingUserInputs.length, diff --git a/apps/web/src/components/chat/folderDrop.test.ts b/apps/web/src/components/chat/folderDrop.test.ts new file mode 100644 index 0000000000..9660a634f0 --- /dev/null +++ b/apps/web/src/components/chat/folderDrop.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, it } from "@effect/vitest"; +import { EnvironmentId } from "@t3tools/contracts"; +import { folderDropTarget, resolveDroppedFolderPath } from "./folderDrop"; + +const environmentId = EnvironmentId.make("environment-1"); + +describe("folderDropTarget", () => { + it("targets local when the thread is on the primary environment", () => { + expect( + folderDropTarget({ + desktopManagedPrimary: true, + primaryRunningDistro: null, + hasNativePathBridge: true, + environmentId, + primaryEnvironmentId: environmentId, + }), + ).toBe("local"); + }); + + it("refuses a browser or configured remote primary even with matching ids", () => { + expect( + folderDropTarget({ + desktopManagedPrimary: false, + primaryRunningDistro: null, + hasNativePathBridge: true, + environmentId, + primaryEnvironmentId: environmentId, + }), + ).toBe("unavailable"); + }); + + it("refuses an older desktop bridge without native path lookup", () => { + expect( + folderDropTarget({ + desktopManagedPrimary: true, + primaryRunningDistro: null, + hasNativePathBridge: false, + environmentId, + primaryEnvironmentId: environmentId, + }), + ).toBe("unavailable"); + }); + + it("targets remote when the thread lives on another environment", () => { + expect( + folderDropTarget({ + desktopManagedPrimary: true, + primaryRunningDistro: null, + hasNativePathBridge: true, + environmentId: EnvironmentId.make("environment-2"), + primaryEnvironmentId: environmentId, + }), + ).toBe("remote"); + }); + + it("targets remote when no primary environment is known", () => { + expect( + folderDropTarget({ + desktopManagedPrimary: true, + primaryRunningDistro: null, + hasNativePathBridge: true, + environmentId, + primaryEnvironmentId: null, + }), + ).toBe("remote"); + }); + + it("refuses a WSL-backed or unknown primary path namespace", () => { + for (const primaryRunningDistro of ["Ubuntu", undefined]) { + expect( + folderDropTarget({ + desktopManagedPrimary: true, + primaryRunningDistro, + hasNativePathBridge: true, + environmentId, + primaryEnvironmentId: environmentId, + }), + ).toBe("unavailable"); + } + }); +}); + +describe("resolveDroppedFolderPath", () => { + it("returns the native path when the bridge provides it", () => { + const folder = new File([], "contracts"); + expect(resolveDroppedFolderPath(folder, () => "/tmp/project/contracts")).toBe( + "/tmp/project/contracts", + ); + }); + + it("returns null for an outside-folder drop even when the folder name matches a project directory", () => { + const folder = new File([], "contracts"); + expect(resolveDroppedFolderPath(folder, undefined)).toBeNull(); + }); + + it("returns null when the bridge returns an empty path", () => { + const folder = new File([], "contracts"); + expect(resolveDroppedFolderPath(folder, () => "")).toBeNull(); + }); + + it("rejects relative or unreadable paths and accepts Windows absolute paths", () => { + const folder = new File([], "contracts"); + expect(resolveDroppedFolderPath(folder, () => "contracts")).toBeNull(); + expect( + resolveDroppedFolderPath(folder, () => { + throw new Error("unavailable"); + }), + ).toBeNull(); + expect(resolveDroppedFolderPath(folder, () => "C:\\work\\contracts")).toBe( + "C:\\work\\contracts", + ); + }); +}); diff --git a/apps/web/src/components/chat/folderDrop.ts b/apps/web/src/components/chat/folderDrop.ts new file mode 100644 index 0000000000..96471be49a --- /dev/null +++ b/apps/web/src/components/chat/folderDrop.ts @@ -0,0 +1,35 @@ +import type { EnvironmentId } from "@t3tools/contracts"; + +export function folderDropTarget(input: { + desktopManagedPrimary: boolean; + primaryRunningDistro: string | null | undefined; + hasNativePathBridge: boolean; + environmentId: EnvironmentId; + primaryEnvironmentId: EnvironmentId | null; +}): "local" | "remote" | "unavailable" { + if (input.primaryEnvironmentId === null || input.environmentId !== input.primaryEnvironmentId) { + return "remote"; + } + if ( + !input.desktopManagedPrimary || + input.primaryRunningDistro !== null || + !input.hasNativePathBridge + ) + return "unavailable"; + return "local"; +} + +export function resolveDroppedFolderPath( + folder: File, + getPathForFile: ((file: File) => string) | undefined, +): string | null { + try { + const path = getPathForFile?.(folder); + return typeof path === "string" && + (path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path) || path.startsWith("\\\\")) + ? path + : null; + } catch { + return null; + } +} diff --git a/apps/web/src/components/chat/workspaceFileDrop.test.ts b/apps/web/src/components/chat/workspaceFileDrop.test.ts index ec5d074a3e..150d474ad0 100644 --- a/apps/web/src/components/chat/workspaceFileDrop.test.ts +++ b/apps/web/src/components/chat/workspaceFileDrop.test.ts @@ -8,6 +8,7 @@ import { function makeDragEvent(options?: { types?: string[]; files?: File[]; + items?: NonNullable; movedWithinTarget?: boolean; }) { const preventDefault = vi.fn(); @@ -16,6 +17,7 @@ function makeDragEvent(options?: { types: options?.types ?? ["Files"], files: options?.files ?? [], dropEffect: "none", + ...(options?.items === undefined ? {} : { items: options.items }), }, relatedTarget: options?.movedWithinTarget ? ({} as EventTarget) : null, currentTarget: { @@ -29,8 +31,9 @@ function makeDragEvent(options?: { function makeHost() { const setDragActive = vi.fn(); const addFiles = vi.fn(); - const host = { setDragActive, addFiles } satisfies WorkspaceFileDropHost; - return { host, setDragActive, addFiles }; + const addFolders = vi.fn(); + const host = { setDragActive, addFiles, addFolders } satisfies WorkspaceFileDropHost; + return { host, setDragActive, addFiles, addFolders }; } describe("makeWorkspaceFileDropHandlers", () => { @@ -75,4 +78,151 @@ describe("makeWorkspaceFileDropHandlers", () => { expect(setDragActive).toHaveBeenCalledWith(false); expect(addFiles).toHaveBeenCalledWith([file]); }); + + it("routes mixed drops to files and folders", () => { + const file = new File(["contents"], "example.txt", { type: "text/plain" }); + const folder = new File([], "project", { type: "" }); + const directory = { + kind: "file", + getAsFile: () => folder, + webkitGetAsEntry: () => ({ isDirectory: true }), + }; + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ + items: [ + directory, + { + kind: "file", + getAsFile: () => file, + webkitGetAsEntry: () => ({ isDirectory: false }), + }, + ], + }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFiles).toHaveBeenCalledWith([file]); + expect(addFolders).toHaveBeenCalledWith([folder], 0); + }); + + it("does not duplicate FileList entries already classified from drag items", () => { + const file = new File(["contents"], "example.txt", { type: "text/plain" }); + const folder = new File([], "project"); + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ + files: [folder, file], + items: [ + { kind: "file", getAsFile: () => folder, webkitGetAsEntry: () => ({ isDirectory: true }) }, + { kind: "file", getAsFile: () => file, webkitGetAsEntry: () => ({ isDirectory: false }) }, + ], + }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFolders).toHaveBeenCalledWith([folder], 0); + expect(addFiles).toHaveBeenCalledWith([file]); + }); + + it("routes a folder-only drop without attaching files", () => { + const folder = new File([], "project", { type: "" }); + const directory = { + kind: "file", + getAsFile: () => folder, + webkitGetAsEntry: () => ({ isDirectory: true }), + }; + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ items: [directory] }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFiles).not.toHaveBeenCalled(); + expect(addFolders).toHaveBeenCalledWith([folder], 0); + }); + + it("uses files when the browser does not expose drag items", () => { + const file = new File(["contents"], "example.txt", { type: "text/plain" }); + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ files: [file] }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFiles).toHaveBeenCalledWith([file]); + expect(addFolders).not.toHaveBeenCalled(); + }); + + it("keeps an ordinary attachment when its drag item cannot return a File", () => { + const file = new File(["contents"], "example.txt", { type: "text/plain" }); + const folder = new File([], "project"); + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ + files: [folder, file], + items: [ + { kind: "file", getAsFile: () => folder, webkitGetAsEntry: () => ({ isDirectory: true }) }, + { kind: "file", getAsFile: () => null, webkitGetAsEntry: () => ({ isDirectory: false }) }, + ], + }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFolders).toHaveBeenCalledWith([folder], 0); + expect(addFiles).toHaveBeenCalledWith([file]); + }); + + it("reports an unreadable directory without attaching its FileList placeholder", () => { + const placeholder = new File([], "project"); + const file = new File(["contents"], "example.txt", { type: "text/plain" }); + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ + files: [placeholder, file], + items: [ + { + kind: "file", + getAsFile: () => null, + webkitGetAsEntry: () => ({ isDirectory: true, name: "project" }), + }, + { kind: "file", getAsFile: () => file, webkitGetAsEntry: () => ({ isDirectory: false }) }, + ], + }); + + makeWorkspaceFileDropHandlers(host).onDrop(event); + + expect(addFolders).toHaveBeenCalledWith([], 1); + expect(addFiles).toHaveBeenCalledWith([file]); + }); + + it("fails closed on same-name empty file and directory placeholders in either item order", () => { + const placeholder = new File([], "project"); + const emptyFile = new File([], "project"); + const ordinary = new File(["contents"], "readme.txt", { type: "text/plain" }); + const directoryItem = { + kind: "file", + getAsFile: () => null, + webkitGetAsEntry: () => ({ isDirectory: true, name: "project" }), + }; + const emptyFileItem = { + kind: "file", + getAsFile: () => null, + webkitGetAsEntry: () => ({ isDirectory: false, name: "project" }), + }; + for (const items of [ + [directoryItem, emptyFileItem], + [emptyFileItem, directoryItem], + ]) { + const { host, addFiles, addFolders } = makeHost(); + const { event } = makeDragEvent({ + files: [placeholder, emptyFile, ordinary], + items: [ + ...items, + { + kind: "file", + getAsFile: () => ordinary, + webkitGetAsEntry: () => ({ isDirectory: false, name: "readme.txt" }), + }, + ], + }); + makeWorkspaceFileDropHandlers(host).onDrop(event); + expect(addFolders).toHaveBeenCalledWith([], 1); + expect(addFiles).toHaveBeenCalledWith([ordinary]); + } + }); }); diff --git a/apps/web/src/components/chat/workspaceFileDrop.ts b/apps/web/src/components/chat/workspaceFileDrop.ts index 132a8051e1..343e36f32e 100644 --- a/apps/web/src/components/chat/workspaceFileDrop.ts +++ b/apps/web/src/components/chat/workspaceFileDrop.ts @@ -2,6 +2,11 @@ export interface WorkspaceFileDragEvent { readonly dataTransfer: { readonly types: ReadonlyArray; readonly files: Iterable; + readonly items?: Iterable<{ + readonly kind: string; + getAsFile(): File | null; + webkitGetAsEntry?(): { readonly isDirectory: boolean; readonly name?: string } | null; + }>; dropEffect: string; }; readonly relatedTarget: EventTarget | null; @@ -14,6 +19,7 @@ export interface WorkspaceFileDragEvent { export interface WorkspaceFileDropHost { setDragActive(active: boolean): void; addFiles(files: File[]): void; + addFolders(folders: File[], unresolvedCount: number): void; } function isFileDrag(event: WorkspaceFileDragEvent): boolean { @@ -24,6 +30,63 @@ function movedWithinDropTarget(event: WorkspaceFileDragEvent): boolean { return event.relatedTarget !== null && event.currentTarget.contains(event.relatedTarget as Node); } +function splitDroppedItems(dataTransfer: WorkspaceFileDragEvent["dataTransfer"]): { + files: File[]; + folders: File[]; + unresolvedFolders: number; +} { + if (dataTransfer.items === undefined) + return { files: Array.from(dataTransfer.files), folders: [], unresolvedFolders: 0 }; + + const files: File[] = []; + const folders: File[] = []; + let unresolvedFolders = 0; + let unresolvedUnnamedFolders = 0; + const unresolvedNames = new Set(); + const seen = new Map(); + const fileKey = (file: File) => + JSON.stringify([file.name, file.size, file.lastModified, file.type]); + for (const item of dataTransfer.items) { + if (item.kind !== "file") continue; + const entry = item.webkitGetAsEntry?.(); + const file = item.getAsFile(); + if (file === null) { + if (entry?.isDirectory) { + unresolvedFolders += 1; + if (entry.name) unresolvedNames.add(entry.name); + else unresolvedUnnamedFolders += 1; + } + continue; + } + const key = fileKey(file); + seen.set(key, (seen.get(key) ?? 0) + 1); + if (entry?.isDirectory === true) { + folders.push(file); + } else { + files.push(file); + } + } + // Some browsers expose a FileList entry while an item cannot return its File object. Keep + // those ordinary attachments instead of silently dropping them. Counts avoid duplicating + // already classified files, including distinct files with the same name and size. + for (const file of dataTransfer.files) { + const key = fileKey(file); + const count = seen.get(key) ?? 0; + if (count > 0) { + seen.set(key, count - 1); + continue; + } + // A FileList placeholder and a distinct empty file can be indistinguishable when both + // drag items return null. Exclude all ambiguous candidates and report the failed drop. + if (unresolvedNames.has(file.name) && file.size === 0 && file.type === "") continue; + if (unresolvedUnnamedFolders > 0 && file.size === 0 && file.type === "") { + continue; + } + files.push(file); + } + return { files, folders, unresolvedFolders }; +} + export function makeWorkspaceFileDropHandlers(host: WorkspaceFileDropHost) { return { onDragEnter(event: WorkspaceFileDragEvent) { @@ -48,7 +111,9 @@ export function makeWorkspaceFileDropHandlers(host: WorkspaceFileDropHost) { if (!isFileDrag(event)) return; event.preventDefault(); host.setDragActive(false); - host.addFiles(Array.from(event.dataTransfer.files)); + const { files, folders, unresolvedFolders } = splitDroppedItems(event.dataTransfer); + if (files.length > 0) host.addFiles(files); + if (folders.length > 0 || unresolvedFolders > 0) host.addFolders(folders, unresolvedFolders); }, }; } diff --git a/packages/contracts/src/ipc.ts b/packages/contracts/src/ipc.ts index f0ba7d2cf0..d2dcef1f4e 100644 --- a/packages/contracts/src/ipc.ts +++ b/packages/contracts/src/ipc.ts @@ -1270,6 +1270,8 @@ export interface DesktopBridge { ) => () => void; getAppBranding: () => DesktopAppBranding | null; + /** Absolute path of a dropped or picked file; absent on desktop builds predating it. */ + getPathForFile?: (file: File) => string; /** The desktop client's OS platform, read from Electron's preload process. */ getClientPlatform?: () => string; setNotificationBadge?: (badge: { count: number; image: string | null }) => Promise; From ddd0f75397c3c081926edd759352adad1e6f4dab Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:25:46 -0600 Subject: [PATCH 084/104] feat(mobile): search projects when starting a task (#796) --- .../features/threads/NewTaskRouteScreen.tsx | 63 ++++++++++++++++++- .../new-task-project-selection.test.ts | 31 +++++++++ .../threads/new-task-project-selection.ts | 17 +++++ 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/apps/mobile/src/features/threads/NewTaskRouteScreen.tsx b/apps/mobile/src/features/threads/NewTaskRouteScreen.tsx index 3a6f6ed7a9..96d2cb87b8 100644 --- a/apps/mobile/src/features/threads/NewTaskRouteScreen.tsx +++ b/apps/mobile/src/features/threads/NewTaskRouteScreen.tsx @@ -7,8 +7,16 @@ import { } from "@react-navigation/native"; import { SymbolView } from "../../components/AppSymbol"; import type { EnvironmentProject } from "@t3tools/client-runtime/state/shell"; -import { useEffect, useRef } from "react"; -import { ActivityIndicator, Alert, Platform, Pressable, ScrollView, View } from "react-native"; +import { useEffect, useRef, useState } from "react"; +import { + ActivityIndicator, + Alert, + Platform, + Pressable, + ScrollView, + TextInput, + View, +} from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { cn } from "../../lib/cn"; @@ -22,6 +30,7 @@ import { useAdaptiveWorkspaceLayout } from "../layout/AdaptiveWorkspaceLayout"; import { useIncomingShare } from "../sharing/IncomingShareProvider"; import { useNewTaskFlow } from "./new-task-flow-provider"; import { + filterProjectScopes, getProjectScopeAccessibilityLabel, getProjectScopeSelectionTarget, } from "./new-task-project-selection"; @@ -87,6 +96,7 @@ function deriveProjectEmptyState(catalogState: WorkspaceState): { export function NewTaskRouteScreen({ route }: StaticScreenProps) { const projects = useProjects(); + const [searchText, setSearchText] = useState(""); const { projectScopes, selectedEnvironmentId, setProject } = useNewTaskFlow(); const { state: catalogState } = useWorkspaceState(); const navigation = useNavigation(); @@ -107,6 +117,7 @@ export function NewTaskRouteScreen({ route }: StaticScreenProps(null); const reservedDestinationProject = incomingShare?.destination ? (projects.find( @@ -230,9 +241,46 @@ export function NewTaskRouteScreen({ route }: StaticScreenProps )} + + + + + {searchText.length > 0 ? ( + setSearchText("")} + > + + + ) : null} + + + )} + ) : visibleScopes.length === 0 ? ( + + + No matching projects + + + Try a different project name or workspace path. + + ) : ( - {projectScopes.map((scope, scopeIndex) => { + {visibleScopes.map((scope, scopeIndex) => { const hasMultipleProjects = scope.projects.length > 1; const selectionTarget = getProjectScopeSelectionTarget(scope, selectedEnvironmentId); return ( diff --git a/apps/mobile/src/features/threads/new-task-project-selection.test.ts b/apps/mobile/src/features/threads/new-task-project-selection.test.ts index 4ed3cbfde6..c78c056d16 100644 --- a/apps/mobile/src/features/threads/new-task-project-selection.test.ts +++ b/apps/mobile/src/features/threads/new-task-project-selection.test.ts @@ -4,12 +4,43 @@ import { describe, expect, it } from "vite-plus/test"; import type { EnvironmentProject } from "@t3tools/client-runtime/state/shell"; import type { HomeProjectScope } from "../home/homeThreadList"; import { + filterProjectScopes, getProjectScopeAccessibilityLabel, getProjectScopeSelectionTarget, resolveDraftProjectSelection, resolveEnvironmentProjectMatch, } from "./new-task-project-selection"; +describe("filterProjectScopes", () => { + const local = makeProject("code", "mac", { title: "Desktop checkout" }); + const remote = makeProject("remote-code", "server", { + workspaceRoot: "/srv/remote-workspace", + }); + const code = makeScope([local, remote]); + const docs = { ...makeScope([makeProject("docs")]), key: "docs", title: "Documentation" }; + const scopes = [code, docs]; + + it("keeps all scopes for an empty or whitespace query", () => { + expect(filterProjectScopes(scopes, "")).toBe(scopes); + expect(filterProjectScopes(scopes, " ")).toBe(scopes); + }); + + it("finds logical names, project titles and workspace paths without case sensitivity", () => { + expect(filterProjectScopes(scopes, " T3 CODE ")).toEqual([code]); + expect(filterProjectScopes(scopes, "DESKTOP")).toEqual([code]); + expect(filterProjectScopes(scopes, "REMOTE-WORKSPACE")).toEqual([code]); + expect(filterProjectScopes(scopes, "documentation")).toEqual([docs]); + expect(filterProjectScopes(scopes, "missing-project")).toEqual([]); + }); + + it("keeps every workspace and the preferred environment when one workspace matches", () => { + const matches = filterProjectScopes(scopes, "REMOTE-WORKSPACE"); + expect(matches[0]).toBe(code); + expect(getProjectScopeSelectionTarget(matches[0]!, EnvironmentId.make("mac"))).toBe(local); + expect(code.projects).toEqual([local, remote]); + }); +}); + function makeProject( id: string, environmentId = "environment", diff --git a/apps/mobile/src/features/threads/new-task-project-selection.ts b/apps/mobile/src/features/threads/new-task-project-selection.ts index 05fc9e2cfe..4b7aecce04 100644 --- a/apps/mobile/src/features/threads/new-task-project-selection.ts +++ b/apps/mobile/src/features/threads/new-task-project-selection.ts @@ -28,6 +28,23 @@ export function getProjectScopeAccessibilityLabel( : `${scope.title}, ${selectionTarget.workspaceRoot}`; } +export function filterProjectScopes( + scopes: ReadonlyArray, + searchText: string, +): ReadonlyArray { + const query = searchText.trim().toLowerCase(); + if (!query) return scopes; + return scopes.filter( + (scope) => + scope.title.toLowerCase().includes(query) || + scope.projects.some( + (project) => + project.title.toLowerCase().includes(query) || + project.workspaceRoot.toLowerCase().includes(query), + ), + ); +} + function getOnlySelectableProject( projectScopes: ReadonlyArray, ): EnvironmentProject | null { From a5a776df40730f2b10903fd4585327e6febe9b8b Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:28:45 -0600 Subject: [PATCH 085/104] feat(mobile): favorite models in thread settings (#790) --- apps/mobile/src/components/AppSymbol.tsx | 4 + .../threads/ThreadSettingsSheet.test.tsx | 47 +++- .../features/threads/ThreadSettingsSheet.tsx | 254 ++++++++++++++---- .../thread-settings-sheet-state.test.ts | 35 ++- .../threads/thread-settings-sheet-state.ts | 35 +++ .../mobile-preferences.favorites.test.ts | 80 ++++++ .../src/persistence/mobile-preferences.ts | 19 +- apps/mobile/src/state/preferences.test.ts | 77 ++++++ apps/mobile/src/state/preferences.ts | 19 +- 9 files changed, 507 insertions(+), 63 deletions(-) create mode 100644 apps/mobile/src/persistence/mobile-preferences.favorites.test.ts diff --git a/apps/mobile/src/components/AppSymbol.tsx b/apps/mobile/src/components/AppSymbol.tsx index aba1023f79..b96c99bb72 100644 --- a/apps/mobile/src/components/AppSymbol.tsx +++ b/apps/mobile/src/components/AppSymbol.tsx @@ -76,6 +76,8 @@ import IconServer from "@tabler/icons-react-native/IconServer"; import IconSettings from "@tabler/icons-react-native/IconSettings"; import IconSparkles from "@tabler/icons-react-native/IconSparkles"; import IconStack2 from "@tabler/icons-react-native/IconStack2"; +import IconStar from "@tabler/icons-react-native/IconStar"; +import IconStarFilled from "@tabler/icons-react-native/IconStarFilled"; import IconStethoscope from "@tabler/icons-react-native/IconStethoscope"; import IconSun from "@tabler/icons-react-native/IconSun"; import IconLayoutSidebarRight from "@tabler/icons-react-native/IconLayoutSidebarRight"; @@ -172,6 +174,8 @@ const ANDROID_ICON_BY_SF_SYMBOL: Partial> = { "square.and.pencil": IconEdit, "square.grid.2x2": IconApps, "square.split.2x1": IconLayoutColumns, + star: IconStar, + "star.fill": IconStarFilled, "sun.max": IconSun, "stop.fill": IconPlayerStopFilled, terminal: IconTerminal2, diff --git a/apps/mobile/src/features/threads/ThreadSettingsSheet.test.tsx b/apps/mobile/src/features/threads/ThreadSettingsSheet.test.tsx index a7bd2cd1fd..19f4ed9a33 100644 --- a/apps/mobile/src/features/threads/ThreadSettingsSheet.test.tsx +++ b/apps/mobile/src/features/threads/ThreadSettingsSheet.test.tsx @@ -43,6 +43,10 @@ vi.mock("../../native/StackHeader", () => ({ })); vi.mock("../../native/native-glass", () => ({ NATIVE_LIQUID_GLASS_SUPPORTED: true })); vi.mock("../../state/server", () => ({ serverEnvironment: {} })); +vi.mock("../../state/preferences", () => ({ + mobilePreferencesAtom: {}, + updateMobilePreferencesAtom: {}, +})); vi.mock("../../state/use-atom-command", () => ({ useAtomCommand: vi.fn() })); vi.mock("./new-task-flow-provider", () => ({ useNewTaskFlow: vi.fn() })); vi.mock("../settings/appearance/AppearancePreferencesProvider", () => ({ @@ -59,6 +63,11 @@ import { ModelRow } from "./ThreadSettingsSheet"; type ElementProps = { readonly children?: ReactNode; readonly numberOfLines?: number; + readonly accessibilityRole?: string; + readonly accessibilityLabel?: string; + readonly accessibilityHint?: string; + readonly accessibilityState?: { readonly disabled?: boolean; readonly selected?: boolean }; + readonly onPress?: () => void; }; function elements(node: ReactNode): ReadonlyArray> { if (node === null || node === undefined || typeof node !== "object") return []; @@ -80,7 +89,20 @@ const option: ModelOption = { capabilities: null, selection: { instanceId: ProviderInstanceId.make("primeAgent"), model: "default" }, }; -const rowProps = { option, selected: false, isFirst: true, isLast: true, onPress: vi.fn() }; +const rowProps = { + option, + selected: false, + isFirst: true, + isLast: true, + onPress: vi.fn(), + isFavorite: false, + favoritesLoaded: true, + onToggleFavorite: vi.fn(), +}; + +function modelChoice(row: ReactElement) { + return elements(row).find((element) => element.props.accessibilityRole === "radio")!; +} describe("ThreadSettingsSheet model row", () => { it.each([PRIME_AGENT_DEFAULT_MODEL_CHANGE_DESCRIPTION, STARTED_THREAD_MODEL_CHANGE_DESCRIPTION])( @@ -92,19 +114,19 @@ describe("ThreadSettingsSheet model row", () => { expect(captions).toHaveLength(1); // The native Text must be allowed to measure all lines of the new-thread remedy. expect(captions[0]?.props.numberOfLines).toBeUndefined(); - expect(row.props).toMatchObject({ + expect(modelChoice(row).props).toMatchObject({ accessibilityLabel: `${option.label}, ${option.subtitle}, ${disabledReason}`, accessibilityRole: "radio", accessibilityState: { checked: false, disabled: true }, disabled: true, }); - expect(row.props.accessibilityHint).toBeUndefined(); + expect(modelChoice(row).props.accessibilityHint).toBeUndefined(); }, ); it("keeps the selected allowed row selectable without a stale reason", () => { const row = ModelRow({ ...rowProps, selected: true }); - expect(row.props).toMatchObject({ + expect(modelChoice(row).props).toMatchObject({ accessibilityLabel: `${option.label}, ${option.subtitle}`, accessibilityRole: "radio", accessibilityState: { checked: true, disabled: false }, @@ -117,7 +139,22 @@ describe("ThreadSettingsSheet model row", () => { expect(text).toContain(option.label); expect(text).toContain(option.subtitle); expect(text.join(" ")).not.toContain("Start a new thread"); - row.props.onPress(); + modelChoice(row).props.onPress?.(); expect(rowProps.onPress).toHaveBeenCalledOnce(); }); + + it("keeps the favorite action separate from selecting a model", () => { + rowProps.onPress.mockClear(); + rowProps.onToggleFavorite.mockClear(); + const row = ModelRow(rowProps); + const favorite = elements(row).find( + (element) => + element.props.accessibilityLabel === + `Add to favorites: ${option.providerLabel}, ${option.label}`, + )!; + expect(favorite.props.accessibilityState).toMatchObject({ disabled: false, selected: false }); + favorite.props.onPress?.(); + expect(rowProps.onToggleFavorite).toHaveBeenCalledOnce(); + expect(rowProps.onPress).not.toHaveBeenCalled(); + }); }); diff --git a/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx b/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx index 2b831d8999..0beca2d6ea 100644 --- a/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx +++ b/apps/mobile/src/features/threads/ThreadSettingsSheet.tsx @@ -1,11 +1,13 @@ import type { EnvironmentId, ModelSelection, + ProviderInstanceId, ProviderOptionDescriptor, ProviderOptionSelection, RuntimeMode, } from "@t3tools/contracts"; import type { LegendListRenderItemProps } from "@legendapp/list/react-native"; +import { useAtomSet, useAtomValue } from "@effect/atom-react"; import { AnimatedLegendList } from "@legendapp/list/reanimated"; import { HeaderHeightContext } from "@react-navigation/elements"; import { @@ -19,6 +21,7 @@ import { type NativeStackNavigationProp, } from "@react-navigation/native-stack"; import * as Haptics from "expo-haptics"; +import { AsyncResult } from "effect/unstable/reactivity"; import { createContext, use, @@ -49,6 +52,7 @@ import { } from "../../native/StackHeader"; import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass"; import { serverEnvironment } from "../../state/server"; +import { mobilePreferencesAtom, updateMobilePreferencesAtom } from "../../state/preferences"; import { useAtomCommand } from "../../state/use-atom-command"; import { useNewTaskFlow } from "./new-task-flow-provider"; import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; @@ -67,9 +71,12 @@ import { selectableChoices, } from "./thread-settings-options"; import { + favoritesFirst, + modelFavoriteKey, modelMatchesCatalogQuery, pendingModelAfterPress, providerSectionIsCollapsed, + toggleModelFavorite, } from "./thread-settings-sheet-state"; /** @@ -96,73 +103,103 @@ const THREAD_SETTINGS_HEADER_SCROLL_EDGE_EFFECTS = nativeHeaderScrollEdgeEffects Platform.OS, Platform.Version, ); +const EMPTY_MODEL_FAVORITES: ReadonlyArray<{ + readonly provider: ProviderInstanceId; + readonly model: string; +}> = []; +const FAVORITES_PROVIDER_FILTER = "@favorites"; export function ModelRow(props: { readonly option: ModelOption; readonly selected: boolean; readonly disabledReason?: string | undefined; readonly onPress: () => void; + readonly isFavorite: boolean; + readonly favoritesLoaded: boolean; + readonly onToggleFavorite: () => void; readonly isFirst: boolean; readonly isLast: boolean; }) { const { materialYouStyleLayoutActive } = useAppearancePreferences(); const selectedMaterialRow = materialYouStyleLayoutActive && props.selected; return ( - - - - - {props.option.label} - - {props.option.isDefault ? ( - - Default - + + + + + {props.option.label} + + {props.option.isDefault ? ( + + Default + + ) : null} + {props.option.isLegacy ? ( + + Legacy + + ) : null} + + {props.option.subtitle ? ( + + {props.option.subtitle} + ) : null} - {props.option.isLegacy ? ( - - Legacy - + {props.disabledReason ? ( + + {props.disabledReason} + ) : null} - {props.option.subtitle ? ( - - {props.option.subtitle} - - ) : null} - {props.disabledReason ? ( - - {props.disabledReason} - + {props.selected ? ( + ) : null} - - {props.selected ? ( + + - ) : null} - + + ); } @@ -373,6 +410,9 @@ export function useExistingThreadSettingsRoutePresentation() { type ThreadSettingsSessionValue = { readonly environmentId: EnvironmentId | null; readonly providerGroups: ReadonlyArray; + readonly favoriteKeys: ReadonlySet; + readonly favoritesLoaded: boolean; + readonly toggleFavorite: (option: ModelOption) => void; readonly runtimeMode: RuntimeMode; readonly runtimeChoices: typeof RUNTIME_MODE_CHOICES; readonly onUpdateRuntimeMode: (mode: RuntimeMode) => void; @@ -401,6 +441,34 @@ const ThreadSettingsSessionContext = createContext + new Set( + modelFavorites.map((favorite) => modelFavoriteKey(favorite.provider, favorite.model)), + ), + [modelFavorites], + ); + const toggleFavorite = useCallback( + (option: ModelOption) => { + if (!favoritesLoaded) return; + void Haptics.selectionAsync(); + savePreferences({ + transform: (current) => ({ + modelFavorites: toggleModelFavorite( + current.modelFavorites ?? EMPTY_MODEL_FAVORITES, + option, + ), + }), + }); + }, + [favoritesLoaded, savePreferences], + ); const [showLegacyToggle, setShowLegacyToggle] = useState(false); const [providerFilter, setProviderFilter] = useState(null); const [searchQuery, setSearchQuery] = useState(""); @@ -506,6 +574,9 @@ function ThreadSettingsSessionProvider( () => ({ environmentId: props.environmentId, providerGroups: props.providerGroups, + favoriteKeys, + favoritesLoaded, + toggleFavorite, runtimeMode: displayedRuntimeMode, runtimeChoices, onUpdateRuntimeMode: props.onUpdateRuntimeMode, @@ -531,6 +602,8 @@ function ThreadSettingsSessionProvider( applyOptionChange, commitPendingModel, displayedDescriptors, + favoriteKeys, + favoritesLoaded, providerExpansionOverrides, hasLegacyModels, isApplied, @@ -547,6 +620,7 @@ function ThreadSettingsSessionProvider( searchQuery, showLegacyToggle, toggleProvider, + toggleFavorite, ], ); @@ -613,6 +687,9 @@ function ThreadSettingsModelListRow(props: { isFirst={props.isFirst} isLast={props.isLast} onPress={onPress} + isFavorite={session.favoriteKeys.has(props.option.key)} + favoritesLoaded={session.favoritesLoaded} + onToggleFavorite={() => session.toggleFavorite(props.option)} option={props.option} selected={session.isDisplayed(props.option)} disabledReason={ @@ -652,19 +729,35 @@ function useThreadSettingsCatalogItems( return useMemo( () => session.providerGroups.flatMap((group) => { - if (session.providerFilter !== null && group.providerKey !== session.providerFilter) { + if ( + session.providerFilter !== null && + session.providerFilter !== FAVORITES_PROVIDER_FILTER && + group.providerKey !== session.providerFilter + ) { return []; } const driver = group.models[0]?.providerDriver; - const catalogModels = session.showLegacy - ? group.models - : group.models.filter((model) => !model.isLegacy || session.isDisplayed(model)); - const visibleModels = catalogModels.filter((model) => - modelMatchesCatalogQuery({ - model, - providerLabel: group.providerLabel, - query: session.searchQuery, - }), + const catalogModels = + session.showLegacy || session.providerFilter === FAVORITES_PROVIDER_FILTER + ? group.models + : group.models.filter( + (model) => + !model.isLegacy || + session.isDisplayed(model) || + session.favoriteKeys.has(model.key), + ); + const visibleModels = favoritesFirst( + catalogModels.filter( + (model) => + (session.providerFilter !== FAVORITES_PROVIDER_FILTER || + session.favoriteKeys.has(model.key)) && + modelMatchesCatalogQuery({ + model, + providerLabel: group.providerLabel, + query: session.searchQuery, + }), + ), + session.favoriteKeys, ); if (visibleModels.length === 0) { return []; @@ -708,6 +801,7 @@ function useThreadSettingsCatalogItems( [ session.isApplied, session.isDisplayed, + session.favoriteKeys, session.providerExpansionOverrides, session.providerFilter, session.providerGroups, @@ -838,7 +932,12 @@ function ThreadSettingsMainContent(props: { } else if (item.kind === "empty") { content = ( - No matching models + + {session.providerFilter === FAVORITES_PROVIDER_FILTER && + session.searchQuery.trim().length === 0 + ? "No favorite models" + : "No matching models"} + ); } else { @@ -860,7 +959,7 @@ function ThreadSettingsMainContent(props: { ); }, - [animationsReady, props.onOpenSubmenu], + [animationsReady, props.onOpenSubmenu, session.providerFilter, session.searchQuery], ); return ( @@ -893,6 +992,38 @@ function ThreadSettingsMainContent(props: { placeholderTextColorClassName="accent-placeholder" value={session.searchQuery} /> + + session.setProviderFilter(null)} + > + All + + session.setProviderFilter(FAVORITES_PROVIDER_FILTER)} + > + Favorites + + ) : null} @@ -1047,6 +1178,15 @@ function ThreadSettingsModelsScreen() { state: session.providerFilter === null ? ("on" as const) : ("off" as const), onPress: () => session.setProviderFilter(null), }, + { + type: "action" as const, + title: "Favorites", + state: + session.providerFilter === FAVORITES_PROVIDER_FILTER + ? ("on" as const) + : ("off" as const), + onPress: () => session.setProviderFilter(FAVORITES_PROVIDER_FILTER), + }, ...session.providerGroups.map((group) => ({ type: "action" as const, title: group.providerLabel, @@ -1180,6 +1320,12 @@ function ThreadSettingsModelsScreen() { > All providers + session.setProviderFilter(FAVORITES_PROVIDER_FILTER)} + > + Favorites + {session.providerGroups.map((group) => ( { + it("keeps favorites in catalog order ahead of other models", () => { + const models = [modelOption("first"), modelOption("second"), modelOption("third")]; + const favorites = new Set([models[2]!.key, models[0]!.key]); + expect(favoritesFirst(models, favorites).map((model) => model.selection.model)).toEqual([ + "first", + "third", + "second", + ]); + expect(models.map((model) => model.selection.model)).toEqual(["first", "second", "third"]); + }); + + it("adds and removes favorites by provider instance", () => { + const codexModel = modelOption("shared"); + const personalId = ProviderInstanceId.make("codex_personal"); + const personalModel = { + ...codexModel, + key: modelFavoriteKey(personalId, "shared"), + selection: { ...codexModel.selection, instanceId: personalId }, + }; + const favorites = toggleModelFavorite([], codexModel); + expect(toggleModelFavorite(favorites, personalModel)).toEqual([ + { provider: ProviderInstanceId.make("codex"), model: "shared" }, + { provider: personalId, model: "shared" }, + ]); + expect(toggleModelFavorite(favorites, codexModel)).toEqual([]); + }); + it("matches visible model and provider terms", () => { const model = modelOption("gpt-next"); diff --git a/apps/mobile/src/features/threads/thread-settings-sheet-state.ts b/apps/mobile/src/features/threads/thread-settings-sheet-state.ts index 1e417b925d..a4fbb38718 100644 --- a/apps/mobile/src/features/threads/thread-settings-sheet-state.ts +++ b/apps/mobile/src/features/threads/thread-settings-sheet-state.ts @@ -1,5 +1,40 @@ +import type { ProviderInstanceId } from "@t3tools/contracts"; + import type { ModelOption } from "../../lib/modelOptions"; +export type ModelFavorite = { + readonly provider: ProviderInstanceId; + readonly model: string; +}; + +export function modelFavoriteKey(provider: ProviderInstanceId, model: string): string { + return `${provider}:${model}`; +} + +export function toggleModelFavorite( + favorites: ReadonlyArray, + option: ModelOption, +): ReadonlyArray { + const provider = option.selection.instanceId; + const model = option.selection.model; + return favorites.some((favorite) => favorite.provider === provider && favorite.model === model) + ? favorites.filter((favorite) => favorite.provider !== provider || favorite.model !== model) + : [...favorites, { provider, model }]; +} + +/** Keep catalog order within each group when favorites move to the front. */ +export function favoritesFirst( + models: ReadonlyArray, + favoriteKeys: ReadonlySet, +): ReadonlyArray { + const favorites: ModelOption[] = []; + const others: ModelOption[] = []; + for (const model of models) { + (favoriteKeys.has(model.key) ? favorites : others).push(model); + } + return [...favorites, ...others]; +} + /** Match the terms a user can actually see or recognize in the model picker. */ export function modelMatchesCatalogQuery(input: { readonly model: ModelOption; diff --git a/apps/mobile/src/persistence/mobile-preferences.favorites.test.ts b/apps/mobile/src/persistence/mobile-preferences.favorites.test.ts new file mode 100644 index 0000000000..7d4efbe4be --- /dev/null +++ b/apps/mobile/src/persistence/mobile-preferences.favorites.test.ts @@ -0,0 +1,80 @@ +import { ProviderInstanceId } from "@t3tools/contracts"; +import { expect, it } from "@effect/vitest"; +import * as Effect from "effect/Effect"; +import * as Option from "effect/Option"; +import { vi } from "vite-plus/test"; + +vi.mock("expo-secure-store", () => ({ + getItemAsync: vi.fn(), + setItemAsync: vi.fn(), + deleteItemAsync: vi.fn(), +})); + +import { MobileDatabase } from "./mobile-database"; +import { make } from "./mobile-preferences"; +import { MobileSecureStorage } from "./mobile-secure-storage"; + +it.effect("persists favorites across a store restart while loading older preferences", () => + Effect.gen(function* () { + let stored = { payload: JSON.stringify({ themeMode: "dark" }), updatedAt: 1 }; + const unused = Effect.die("Unrelated database operation"); + const database = MobileDatabase.of({ + loadCache: () => unused, + listCache: () => unused, + saveCache: () => unused, + removeCache: () => unused, + clearCacheKind: () => unused, + clearEnvironmentCache: () => unused, + clearAllCaches: unused, + inspectCaches: unused, + loadPreferencesJson: Effect.sync(() => Option.some(stored)), + savePreferencesJson: (payload, updatedAt) => + Effect.sync(() => { + stored = { payload, updatedAt }; + }), + }); + const secureStorage = MobileSecureStorage.of({ + getItem: () => Effect.succeed(null), + setItem: () => Effect.void, + removeItem: () => Effect.void, + }); + const makeStore = () => + make().pipe( + Effect.provideService(MobileDatabase, database), + Effect.provideService(MobileSecureStorage, secureStorage), + ); + + const beforeRestart = yield* makeStore(); + expect(yield* beforeRestart.load).toEqual({ themeMode: "dark" }); + const provider = ProviderInstanceId.make("codex_personal"); + yield* beforeRestart.savePatch({ modelFavorites: [{ provider, model: "gpt-6-sol" }] }); + expect(JSON.parse(stored.payload)).toEqual({ + themeMode: "dark", + modelFavorites: [{ provider, model: "gpt-6-sol" }], + }); + + const afterRestart = yield* makeStore(); + expect(yield* afterRestart.load).toEqual({ + themeMode: "dark", + modelFavorites: [{ provider, model: "gpt-6-sol" }], + }); + + stored = { + payload: JSON.stringify({ + themeMode: "dark", + modelFavorites: [ + { provider, model: "gpt-6-sol" }, + { provider: "", model: "invalid" }, + { provider, model: " " }, + { provider, model: 42 }, + ], + }), + updatedAt: stored.updatedAt + 1, + }; + const sanitizedRestart = yield* makeStore(); + expect(yield* sanitizedRestart.load).toEqual({ + themeMode: "dark", + modelFavorites: [{ provider, model: "gpt-6-sol" }], + }); + }), +); diff --git a/apps/mobile/src/persistence/mobile-preferences.ts b/apps/mobile/src/persistence/mobile-preferences.ts index 8209f6103b..86d5ef8a58 100644 --- a/apps/mobile/src/persistence/mobile-preferences.ts +++ b/apps/mobile/src/persistence/mobile-preferences.ts @@ -5,7 +5,7 @@ import * as Option from "effect/Option"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; -import type { SidebarProjectGroupingMode } from "@t3tools/contracts"; +import type { ProviderInstanceId, SidebarProjectGroupingMode } from "@t3tools/contracts"; import { MOBILE_THEME_IDS, type MobileThemeId, type MobileThemeMode } from "../lib/mobileTheme"; import * as MobileDatabase from "./mobile-database"; @@ -42,6 +42,11 @@ export interface Preferences { readonly legacyThreadListEnabled?: boolean; /** Device-local counterpart of desktop's `planModeEnabled` legacy flag. */ readonly planModeEnabled?: boolean; + /** Model favorites belong to this device, like the web client setting. */ + readonly modelFavorites?: ReadonlyArray<{ + readonly provider: ProviderInstanceId; + readonly model: string; + }>; /** Fresh keys reset both shelves to collapsed when users update. */ readonly threadListSettledShelfExpanded?: boolean; readonly threadListSnoozedShelfExpanded?: boolean; @@ -103,6 +108,7 @@ function sanitizePreferences(parsed: Preferences): Preferences { projectGroupingMode?: SidebarProjectGroupingMode; legacyThreadListEnabled?: boolean; planModeEnabled?: boolean; + modelFavorites?: Preferences["modelFavorites"]; threadListSettledShelfExpanded?: boolean; threadListSnoozedShelfExpanded?: boolean; } = {}; @@ -175,6 +181,17 @@ function sanitizePreferences(parsed: Preferences): Preferences { if (typeof parsed.planModeEnabled === "boolean") { preferences.planModeEnabled = parsed.planModeEnabled; } + if (Array.isArray(parsed.modelFavorites)) { + preferences.modelFavorites = parsed.modelFavorites.filter( + (favorite) => + typeof favorite === "object" && + favorite !== null && + typeof favorite.provider === "string" && + favorite.provider.length > 0 && + typeof favorite.model === "string" && + favorite.model.trim().length > 0, + ); + } if (typeof parsed.threadListSettledShelfExpanded === "boolean") { preferences.threadListSettledShelfExpanded = parsed.threadListSettledShelfExpanded; } diff --git a/apps/mobile/src/state/preferences.test.ts b/apps/mobile/src/state/preferences.test.ts index c53594eb23..9a84d70cd6 100644 --- a/apps/mobile/src/state/preferences.test.ts +++ b/apps/mobile/src/state/preferences.test.ts @@ -4,6 +4,10 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"; import { vi } from "vite-plus/test"; +import { ProviderInstanceId } from "@t3tools/contracts"; +import { RegistryContext, useAtomSet } from "@effect/atom-react"; +import { createElement } from "react"; +import { renderToString } from "react-dom/server"; vi.mock("expo-secure-store", () => ({ getItemAsync: vi.fn(), @@ -124,6 +128,79 @@ describe("mobile preferences state", () => { }), ); + it.effect("keeps both favorites when the React setter sends updates before a render", () => + Effect.gen(function* () { + let persisted: Preferences = { modelFavorites: [] }; + const state = makePreferencesState({ + load: Effect.succeed(persisted), + savePatch: (patch) => + Effect.sync(() => { + persisted = { ...persisted, ...patch }; + return persisted; + }), + update: (transform) => + Effect.sync(() => { + persisted = { ...persisted, ...transform(persisted) }; + return persisted; + }), + }); + const registry = AtomRegistry.make(); + const unmountPreferences = registry.mount(state.preferencesAtom); + const unmountUpdate = registry.mount(state.updatePreferencesAtom); + yield* AtomRegistry.getResult(registry, state.preferencesAtom, { suspendOnWaiting: true }); + + function useSavePreferences() { + return useAtomSet(state.updatePreferencesAtom); + } + const setters: Array> = []; + function CaptureSetter() { + setters.push(useSavePreferences()); + return null; + } + // Exercise the real React setter, which treats bare functions as updates + // to the atom's read value. Direct registry.set calls bypass that behavior. + renderToString( + createElement(RegistryContext.Provider, { value: registry }, createElement(CaptureSetter)), + ); + const savePreferences = setters[0]!; + const provider = ProviderInstanceId.make("codex"); + savePreferences({ + transform: (current) => ({ + modelFavorites: [...(current.modelFavorites ?? []), { provider, model: "astra" }], + }), + }); + savePreferences({ + transform: (current) => ({ + modelFavorites: [...(current.modelFavorites ?? []), { provider, model: "sol" }], + }), + }); + yield* AtomRegistry.getResult(registry, state.updatePreferencesAtom, { + suspendOnWaiting: true, + }); + + expect(persisted.modelFavorites).toEqual([ + { provider, model: "astra" }, + { provider, model: "sol" }, + ]); + + savePreferences({ + transform: (current) => ({ + modelFavorites: (current.modelFavorites ?? []).filter( + (favorite) => favorite.model !== "astra", + ), + }), + }); + yield* AtomRegistry.getResult(registry, state.updatePreferencesAtom, { + suspendOnWaiting: true, + }); + expect(persisted.modelFavorites).toEqual([{ provider, model: "sol" }]); + + unmountUpdate(); + unmountPreferences(); + registry.dispose(); + }), + ); + it.effect("falls back to empty preferences when secure storage cannot be read", () => Effect.gen(function* () { const state = makePreferencesState({ diff --git a/apps/mobile/src/state/preferences.ts b/apps/mobile/src/state/preferences.ts index d173cf55be..7baff0251c 100644 --- a/apps/mobile/src/state/preferences.ts +++ b/apps/mobile/src/state/preferences.ts @@ -15,6 +15,12 @@ interface OptimisticPreferences { readonly versions: Partial>; } +// A bare function is interpreted by useAtomSet as an update to the command's +// AsyncResult, not as a preference transform. Keep transforms inside a payload. +type PreferencesUpdate = + | Partial + | { readonly transform: (current: Preferences) => Partial }; + /** * Owns the device preference blob for the lifetime of the app registry. * Optimistic patches are kept separately so writes made while persistence is @@ -57,7 +63,14 @@ export function createMobilePreferencesState(runtime: Atom.AtomRuntime, get) => { + (update: PreferencesUpdate, get) => { + const currentPreferences = get(preferencesAtom); + const patch = + "transform" in update + ? update.transform( + AsyncResult.isSuccess(currentPreferences) ? currentPreferences.value : {}, + ) + : update; const version = ++nextPatchVersion; const current = get(optimisticPatchAtom); const versions = { ...current.versions }; @@ -69,7 +82,9 @@ export function createMobilePreferencesState(runtime: Atom.AtomRuntime store.savePatch(patch)), + Effect.flatMap((store) => + "transform" in update ? store.update(update.transform) : store.savePatch(patch), + ), Effect.tap((saved) => Effect.sync(() => { get.set(confirmedPreferencesAtom, saved); From 0a4fa624bd540d1e1246fe0f1a47ca0c2854a8c0 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:29:16 -0600 Subject: [PATCH 086/104] test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture --- .../server/src/provider/prime/PrimeAgentDaemonManager.test.ts | 4 +++- apps/server/src/sourceControl/PrTemplateDetection.test.ts | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts b/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts index aecfc50823..ee2f0c00d7 100644 --- a/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts +++ b/apps/server/src/provider/prime/PrimeAgentDaemonManager.test.ts @@ -944,7 +944,9 @@ describe("PrimeAgentDaemonManager lifecycle", () => { shutdownRejects: true, shutdownDoesNotExit: true, postKillExitHangs: true, - shutdownTimeout: Duration.millis(5), + // This timeout also covers real private-socket cleanup before the exit wait. + // Leave room for filesystem scheduling under parallel CI load. + shutdownTimeout: Duration.millis(250), }); return closeFixtureLive(fixture).pipe( Effect.andThen( diff --git a/apps/server/src/sourceControl/PrTemplateDetection.test.ts b/apps/server/src/sourceControl/PrTemplateDetection.test.ts index 5b91e63111..410981603d 100644 --- a/apps/server/src/sourceControl/PrTemplateDetection.test.ts +++ b/apps/server/src/sourceControl/PrTemplateDetection.test.ts @@ -55,6 +55,9 @@ const runWithTempDirectory = ( const fileSystem = yield* FileSystem.FileSystem; const cwd = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-pr-template-" }); yield* runGit(cwd, ["init", "--initial-branch=main"]); + // Auto maintenance may still write object packs after the scoped fixture + // starts removing this temporary repository under parallel CI load. + yield* runGit(cwd, ["config", "gc.auto", "0"]); yield* runGit(cwd, ["config", "user.email", "test@example.com"]); yield* runGit(cwd, ["config", "user.name", "Test User"]); return yield* test(cwd); From 4e1514646153286524eee4cf65182ab40e07a471 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:34:07 -0600 Subject: [PATCH 087/104] fix(desktop): keep SnapShot shortcut helper out of Dock (#778) --- .../src/snapShot/MacModifierPairShortcutProcess.test.ts | 5 +++++ apps/desktop/src/snapShot/MacModifierPairShortcutProcess.ts | 3 +++ 2 files changed, 8 insertions(+) diff --git a/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.test.ts b/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.test.ts index 81889f9f07..d0548c78c0 100644 --- a/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.test.ts +++ b/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.test.ts @@ -54,6 +54,11 @@ describe("macOS modifier pair poller", () => { const poller = spawnedPollers[0]!; expect(poller.command).toBe("/usr/bin/osascript"); expect(poller.args.slice(-2)).toEqual(["8", "16"]); + const script = poller.args[3]!; + expect(script.indexOf("NSApplicationActivationPolicyProhibited")).toBeGreaterThan(0); + expect(script.indexOf("NSApplicationActivationPolicyProhibited")).toBeLessThan( + script.indexOf('ObjC.import("CoreGraphics")'), + ); poller.emitStderr("ready\ntrig"); const stop = await started; diff --git a/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.ts b/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.ts index 1c638f2444..c869e92efa 100644 --- a/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.ts +++ b/apps/desktop/src/snapShot/MacModifierPairShortcutProcess.ts @@ -12,6 +12,9 @@ const MAC_MODIFIER_PAIR_DEVICE_MASKS: Record Date: Thu, 24 Sep 2026 23:36:24 -0600 Subject: [PATCH 088/104] fix(mobile): wait for deep-link thread hydration (#764) --- .../features/threads/ThreadRouteScreen.tsx | 59 ++++++++-- .../threads/thread-route-hydration.test.ts | 101 ++++++++++++++++++ .../threads/thread-route-hydration.ts | 56 ++++++++++ apps/mobile/src/state/shell.ts | 27 +++++ 4 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 apps/mobile/src/features/threads/thread-route-hydration.test.ts create mode 100644 apps/mobile/src/features/threads/thread-route-hydration.ts diff --git a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx index 729430f1f7..9a0f9491e1 100644 --- a/apps/mobile/src/features/threads/ThreadRouteScreen.tsx +++ b/apps/mobile/src/features/threads/ThreadRouteScreen.tsx @@ -47,6 +47,7 @@ import { import { Alert, Platform, ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useWorkspaceState } from "../../state/workspace"; +import { useEnvironmentShellState } from "../../state/shell"; import { restoredNewTaskDraftKey } from "../../state/new-task-draft-key"; import { clearPendingThreadCreationOutcome } from "../../state/pending-thread-creation"; import { @@ -113,6 +114,7 @@ import { ThreadInspectorContentStack, type ThreadInspectorMode, } from "./thread-inspector-content-stack"; +import { threadRouteIsHydrating, threadRouteRecovery } from "./thread-route-hydration"; interface ThreadInspectorSelection { readonly routeThreadIdentity: string | null; @@ -148,7 +150,11 @@ interface ThreadRouteScreenProps extends ThreadRouteScreenRouteProps { readonly renderInspector?: (headerInset: number) => ReactNode; } -function ThreadUnavailableScreen() { +/** Shows recovery only after the target route has reached a terminal unavailable state. */ +function ThreadUnavailableScreen(props: { + readonly actionLabel: string; + readonly onAction: () => void; +}) { return ( ); @@ -177,6 +185,9 @@ export function ThreadRouteScreen(props: ThreadRouteScreenProps) { const threadIdRaw = firstRouteParam(params.threadId); const environmentId = environmentIdRaw ? EnvironmentId.make(environmentIdRaw) : null; const routeEnvironmentRuntime = useRemoteEnvironmentRuntime(environmentId); + const routeEnvironmentShellState = useEnvironmentShellState(environmentId); + const { onReconnectEnvironment } = useRemoteConnections(); + const navigation = useNavigation(); const routeConnectionState = routeEnvironmentRuntime?.connectionState ?? (environmentId ? "available" : connectionState); const routeThreadKey = @@ -202,16 +213,52 @@ export function ThreadRouteScreen(props: ThreadRouteScreenProps) { return ; } - const stillHydrating = - workspaceState.isLoadingConnections || - routeConnectionState === "connecting" || - routeConnectionState === "reconnecting"; + const stillHydrating = threadRouteIsHydrating({ + isLoadingConnections: workspaceState.isLoadingConnections, + connectionState: routeConnectionState, + shellStatus: routeEnvironmentShellState.status, + shellHasError: Option.isSome(routeEnvironmentShellState.error), + detailStatus: selectedThreadDetailState.status, + detailHasError: Option.isSome(selectedThreadDetailState.error), + }); if (stillHydrating) { return ; } - return ; + const recovery = threadRouteRecovery({ + hasEnvironmentRuntime: routeEnvironmentRuntime !== null, + connectionState: routeConnectionState, + shellStatus: routeEnvironmentShellState.status, + detailStatus: selectedThreadDetailState.status, + detailHasError: Option.isSome(selectedThreadDetailState.error), + }); + + return ( + { + if (recovery === "view-threads") { + navigation.dispatch(StackActions.replace("Home")); + return; + } + if (recovery === "reconnect-environment") { + onReconnectEnvironment(environmentId); + return; + } + navigation.navigate("SettingsSheet", { + screen: "SettingsContent", + params: { screen: "SettingsEnvironments" }, + }); + }} + /> + ); } function ThreadRouteContent( diff --git a/apps/mobile/src/features/threads/thread-route-hydration.test.ts b/apps/mobile/src/features/threads/thread-route-hydration.test.ts new file mode 100644 index 0000000000..e5a2177029 --- /dev/null +++ b/apps/mobile/src/features/threads/thread-route-hydration.test.ts @@ -0,0 +1,101 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { threadRouteIsHydrating, threadRouteRecovery } from "./thread-route-hydration"; + +const settled = { + isLoadingConnections: false, + connectionState: "connected" as const, + shellStatus: "live" as const, + shellHasError: false, + detailStatus: "live" as const, + detailHasError: false, +}; + +describe("threadRouteIsHydrating", () => { + it("waits for shell and thread detail hydration", () => { + expect(threadRouteIsHydrating({ ...settled, shellStatus: "synchronizing" })).toBe(true); + expect(threadRouteIsHydrating({ ...settled, shellStatus: "empty" })).toBe(true); + expect(threadRouteIsHydrating({ ...settled, detailStatus: "synchronizing" })).toBe(true); + expect(threadRouteIsHydrating({ ...settled, detailStatus: "empty" })).toBe(true); + }); + + it("stops waiting once an empty detail has an actionable outcome", () => { + expect( + threadRouteIsHydrating({ + ...settled, + shellStatus: "empty", + shellHasError: true, + detailStatus: "deleted", + }), + ).toBe(false); + expect( + threadRouteIsHydrating({ + ...settled, + detailStatus: "empty", + detailHasError: true, + }), + ).toBe(false); + expect( + threadRouteIsHydrating({ + ...settled, + connectionState: "available", + detailStatus: "empty", + }), + ).toBe(false); + expect(threadRouteIsHydrating({ ...settled, detailStatus: "deleted" })).toBe(false); + }); + + it("prioritizes terminal outcomes over unrelated hydration", () => { + expect( + threadRouteIsHydrating({ + ...settled, + connectionState: "reconnecting", + shellStatus: "synchronizing", + detailStatus: "deleted", + }), + ).toBe(false); + expect( + threadRouteIsHydrating({ + ...settled, + connectionState: "reconnecting", + shellStatus: "synchronizing", + detailHasError: true, + }), + ).toBe(false); + expect( + threadRouteIsHydrating({ + ...settled, + connectionState: "offline", + shellStatus: "synchronizing", + detailStatus: "synchronizing", + }), + ).toBe(false); + }); +}); + +describe("threadRouteRecovery", () => { + const recovery = { + hasEnvironmentRuntime: true, + connectionState: "connected" as const, + shellStatus: "live" as const, + detailStatus: "empty" as const, + detailHasError: false, + }; + + it("returns to the list for a deleted or missing thread", () => { + expect(threadRouteRecovery({ ...recovery, detailStatus: "deleted" })).toBe("view-threads"); + expect(threadRouteRecovery({ ...recovery, detailHasError: true })).toBe("view-threads"); + }); + + it("offers connection recovery for sync failures and settings for an unknown environment", () => { + expect(threadRouteRecovery({ ...recovery, shellStatus: "synchronizing" })).toBe( + "reconnect-environment", + ); + expect(threadRouteRecovery({ ...recovery, connectionState: "offline" })).toBe( + "reconnect-environment", + ); + expect(threadRouteRecovery({ ...recovery, hasEnvironmentRuntime: false })).toBe( + "manage-environments", + ); + }); +}); diff --git a/apps/mobile/src/features/threads/thread-route-hydration.ts b/apps/mobile/src/features/threads/thread-route-hydration.ts new file mode 100644 index 0000000000..d88cde8358 --- /dev/null +++ b/apps/mobile/src/features/threads/thread-route-hydration.ts @@ -0,0 +1,56 @@ +import type { EnvironmentConnectionPhase } from "@t3tools/client-runtime/connection"; +import type { EnvironmentShellStatus } from "@t3tools/client-runtime/state/shell"; +import type { EnvironmentThreadStatus } from "@t3tools/client-runtime/state/threads"; + +/** + * Reports whether the route-local projections can still yield the requested + * thread. Explicit terminal outcomes win over unrelated synchronization. + */ +export function threadRouteIsHydrating(input: { + readonly isLoadingConnections: boolean; + readonly connectionState: EnvironmentConnectionPhase; + readonly shellStatus: EnvironmentShellStatus; + readonly shellHasError: boolean; + readonly detailStatus: EnvironmentThreadStatus; + readonly detailHasError: boolean; +}): boolean { + if (input.detailStatus === "deleted" || input.shellHasError || input.detailHasError) { + return false; + } + if (input.isLoadingConnections) { + return true; + } + if ( + input.connectionState === "available" || + input.connectionState === "offline" || + input.connectionState === "error" + ) { + return false; + } + return ( + input.connectionState === "connecting" || + input.connectionState === "reconnecting" || + input.shellStatus === "synchronizing" || + (input.connectionState === "connected" && input.shellStatus === "empty") || + input.detailStatus === "synchronizing" || + (input.connectionState === "connected" && input.detailStatus === "empty") + ); +} + +/** Picks recovery for a missing route using its authoritative outcome. */ +export function threadRouteRecovery(input: { + readonly hasEnvironmentRuntime: boolean; + readonly connectionState: EnvironmentConnectionPhase; + readonly shellStatus: EnvironmentShellStatus; + readonly detailStatus: EnvironmentThreadStatus; + readonly detailHasError: boolean; +}): "manage-environments" | "view-threads" | "reconnect-environment" { + if (!input.hasEnvironmentRuntime) return "manage-environments"; + if ( + input.detailStatus === "deleted" || + (input.connectionState === "connected" && input.shellStatus === "live" && input.detailHasError) + ) { + return "view-threads"; + } + return "reconnect-environment"; +} diff --git a/apps/mobile/src/state/shell.ts b/apps/mobile/src/state/shell.ts index e879dd25e2..e20209c7cd 100644 --- a/apps/mobile/src/state/shell.ts +++ b/apps/mobile/src/state/shell.ts @@ -3,7 +3,12 @@ import { createEnvironmentShellSummaryAtom, createEnvironmentSnapshotAtom, createShellEnvironmentAtoms, + type EnvironmentShellState, } from "@t3tools/client-runtime/state/shell"; +import { useAtomValue } from "@effect/atom-react"; +import type { EnvironmentId } from "@t3tools/contracts"; +import * as Option from "effect/Option"; +import { AsyncResult, Atom } from "effect/unstable/reactivity"; import { environmentCatalog } from "../connection/catalog"; import { connectionAtomRuntime } from "../connection/runtime"; @@ -15,3 +20,25 @@ export const environmentShellSummaryAtom = createEnvironmentShellSummaryAtom({ catalogValueAtom: environmentCatalog.catalogValueAtom, shellStateValueAtom: environmentShell.stateValueAtom, }); + +const EMPTY_ENVIRONMENT_SHELL_STATE_ATOM = Atom.make( + AsyncResult.success({ + snapshot: Option.none(), + status: "empty", + error: Option.none(), + }), +).pipe(Atom.withLabel("mobile-environment-shell:empty")); + +/** Reads one environment's shell projection without waiting on other environments. */ +export function useEnvironmentShellState(environmentId: EnvironmentId | null) { + const result = useAtomValue( + environmentId === null + ? EMPTY_ENVIRONMENT_SHELL_STATE_ATOM + : environmentShell.stateAtom(environmentId), + ); + return Option.getOrElse(AsyncResult.value(result), () => ({ + snapshot: Option.none(), + status: "empty" as const, + error: Option.none(), + })); +} From 47bf17d2a2a8d19ff4e25f207a093c38b0d00261 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:39:35 -0600 Subject: [PATCH 089/104] fix(relay): stale Relay workers no longer flood a reopened thread (#852) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(relay): stop replaying stale Relay workers into reopened threads The startup receipt scan adopts every Relay MCP tool call ever recorded, so the first boot after the worker bridge shipped bound 284 long-finished jobs across 207 old turns. None could be observed, and each one published an idle "Relay observer unavailable" row into its original turn, dated now -- a wall of stale subagent cards on reopening the thread. An outage row now only explains a worker the thread already started, and an announced outage pushes its recheck back out instead of spawning the Relay CLI on every sweep for the life of the process. * fix(relay): clear the stale Relay outage rows already written The bridge no longer announces a worker it never started, but every projection that booted the earlier build still holds the idle rows it wrote. Drop the outage rows whose task has no start in the same thread, leaving the ones that explain a worker the thread really ran. * fix(relay): retire adopted jobs Relay reports as gone An outage that had already been announced returned before pushing its recheck back out, so every binding kept a Relay CLI spawn on each sweep. Adopted history made that 284 spawns a sweep on one projection. Push the recheck out for a worker that may still return, and retire a binding outright once Relay itself reports the job gone and nothing about it ever reached the thread. Absent Relay says nothing about the job, so that case keeps the binding and only slows the recheck. The four copies of the teardown are now one helper. * fix(relay): type the test engine helper against the SqlClient service * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(relay): retire a lost job for good instead of re-probing every boot Retirement only lived in memory, so every job the environment had ever run was adopted and re-probed on each start -- a cost that grows with history and never settles. Record the retirement on the binding row itself. The activity upserts on its id, so this writes no new row and stays out of the transcript, and boot skips the binding unless an activation was recorded after the watermark. A resume outranks the retirement and wakes the worker, then answers to the ordinary rules again. * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record p… * feat(web): filter sidebar from thread menu (#821) * feat(web): add bounded route history shortcuts on desktop (#803) * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(… * fix(web): add local dropped folders as path chips (#818) * feat(mobile): search projects when starting a task (#796) * feat(mobile): favorite models in thread settings (#790) * test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- .../orchestration/RelayWorkerBridge.test.ts | 343 +++++++++++++++++- .../src/orchestration/RelayWorkerBridge.ts | 191 +++++++--- apps/server/src/persistence/Migrations.ts | 2 + .../063_ClearUnprojectedRelayOutages.test.ts | 65 ++++ .../063_ClearUnprojectedRelayOutages.ts | 28 ++ 5 files changed, 569 insertions(+), 60 deletions(-) create mode 100644 apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.test.ts create mode 100644 apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.ts diff --git a/apps/server/src/orchestration/RelayWorkerBridge.test.ts b/apps/server/src/orchestration/RelayWorkerBridge.test.ts index ab4a135e61..43d1c40910 100644 --- a/apps/server/src/orchestration/RelayWorkerBridge.test.ts +++ b/apps/server/src/orchestration/RelayWorkerBridge.test.ts @@ -1087,16 +1087,14 @@ persistence("Relay persisted observer and controls", (it) => { yield* missingBridge.reconcile; yield* missingBridge.reconcile; yield* missingBridge.reconcile; + // A binding whose worker never reached the thread has nothing to + // downgrade, so its missing Relay files stay quiet rather than + // opening an idle row nobody can act on. const missingRows = yield* sql` SELECT payload_json AS payload FROM projection_thread_activities WHERE kind = 'task.progress' AND json_extract(payload_json, '$.taskId') = ${`relay:${missingId}`} `; - expect(missingRows).toHaveLength(1); - expect(decodeJson(missingRows[0]?.payload)).toMatchObject({ - status: "idle", - summary: "Relay observer unavailable", - cancellable: false, - }); + expect(missingRows).toEqual([]); const missingPolls = cli .calls() .filter((args) => args[0] === "observe" && args[1] === missingId).length; @@ -1195,3 +1193,336 @@ persistence("Relay persisted observer and controls", (it) => { }, ); }); + +function recordingEngine(sql: SqlClient.SqlClient) { + const commands = new Set(); + return { + dispatch: (command: { + readonly type: string; + readonly commandId: string; + readonly threadId: string; + readonly activity?: { + readonly id: string; + readonly turnId: string | null; + readonly kind: string; + readonly tone: string; + readonly summary: string; + readonly payload: unknown; + readonly createdAt: string; + }; + }) => + Effect.gen(function* () { + if (commands.has(command.commandId)) return { sequence: commands.size, eventCount: 0 }; + if (command.type !== "thread.activity.append" || !command.activity) + throw new Error("unexpected command"); + commands.add(command.commandId); + const activity = command.activity; + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES ( + ${activity.id}, ${command.threadId}, ${activity.turnId}, ${activity.tone}, + ${activity.kind}, ${activity.summary}, ${encodeJson(activity.payload)}, ${commands.size}, ${activity.createdAt} + ) ON CONFLICT(activity_id) DO UPDATE SET + payload_json = excluded.payload_json, sequence = excluded.sequence, + created_at = excluded.created_at + `; + return { sequence: commands.size, eventCount: 1 }; + }), + } as unknown as OrchestrationEngineService["Service"]; +} + +persistence("Relay progress-only outage", (it) => { + it.effect("keeps an outage for a progress-only worker after restart", () => { + const cli = fakeRelayCli(); + cli.setState({}); + return Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + const jobId = "job-aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa4"; + const taskId = `relay:${jobId}`; + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES ( + ${`relay-binding:${jobId}`}, 'thread-a', 'turn-progress', 'info', + 'relay.binding', 'Relay worker bound', + ${encodeJson({ kind: "job", id: jobId, toolCallId: "tool-progress", environmentId: "test-environment" })}, + 1, '2026-09-24T00:00:00.000Z' + ), ( + ${`relay-progress:${jobId}`}, 'thread-a', 'turn-progress', 'info', + 'task.progress', 'Relay worker running', + ${encodeJson({ taskId, attempt: 1, relaySequence: 1, status: "running" })}, + 2, '2026-09-24T00:00:01.000Z' + ) + `; + const bridge = yield* makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, recordingEngine(sql)), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, makeLiveness()), + ); + yield* bridge.reconcile; + yield* bridge.reconcile; + yield* bridge.reconcile; + const outage = yield* sql` + SELECT payload_json AS payload FROM projection_thread_activities + WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable' + AND json_extract(payload_json, '$.taskId') = ${taskId} + `; + expect(outage).toHaveLength(1); + expect(decodeJson(outage[0]?.payload)).toMatchObject({ status: "idle" }); + }).pipe(Effect.ensuring(Effect.sync(() => cli.cleanup()))); + }); +}); + +persistence("Relay startup receipt adoption", (it) => { + it.effect("stays silent about adopted historical jobs it never projected", () => { + const cli = fakeRelayCli(); + // The startup scan adopts every persisted Relay receipt, including turns + // whose workers finished and were swept long before this boot. + cli.setState({}); + return Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + const historical = [ + { job: "job-aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa1", turn: "turn-old-1", tool: "tool-old-1" }, + { job: "job-aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa2", turn: "turn-old-2", tool: "tool-old-2" }, + ]; + for (const entry of historical) { + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, turn_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES ( + ${`persisted-${entry.tool}`}, 'thread-a', ${entry.turn}, 'tool', 'tool.completed', + 'Relay delegate', + ${encodeJson({ + itemType: "mcp_tool_call", + toolCallId: entry.tool, + status: "completed", + data: { + toolName: "mcp__plugin_relay-orchestrator_relay__relay_delegate", + result: { + type: "tool_result", + tool_use_id: entry.tool, + content: [ + { type: "text", text: encodeJson({ id: entry.job, status: "queued" }) }, + ], + }, + }, + })}, + NULL, '2026-09-20T00:00:00.000Z' + ) + `; + } + const bridge = yield* makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, recordingEngine(sql)), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, makeLiveness()), + ); + yield* bridge.reconcile; + yield* bridge.reconcile; + yield* bridge.reconcile; + yield* bridge.reconcile; + + // The scan still adopts them so a worker that outlived a crash can be + // reclaimed, but a job it can never observe stays out of the thread. + const adopted = yield* sql` + SELECT activity_id AS activityId FROM projection_thread_activities + WHERE kind = 'relay.binding' ORDER BY activity_id + `; + expect(adopted).toEqual( + historical.map((entry) => ({ activityId: `relay-binding:${entry.job}` })), + ); + const noise = yield* sql` + SELECT activity_id AS activityId FROM projection_thread_activities + WHERE kind LIKE 'task.%' + `; + expect(noise).toEqual([]); + // Relay answered that these jobs are gone and nothing about them ever + // reached the thread, so they are retired instead of holding a CLI spawn + // for the life of the process. + const settledPolls = cli.calls().filter((args) => args[0] === "observe").length; + expect(settledPolls).toBeGreaterThan(0); + yield* bridge.reconcile; + yield* bridge.reconcile; + expect(cli.calls().filter((args) => args[0] === "observe")).toHaveLength(settledPolls); + + // Retirement is persisted on the binding itself, so a restart does not + // re-probe every job the environment has ever run. + const restarted = yield* makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, recordingEngine(sql)), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, makeLiveness()), + ); + const beforeRestart = cli.calls().filter((args) => args[0] === "observe").length; + yield* restarted.reconcile; + yield* restarted.reconcile; + expect( + cli + .calls() + .slice(beforeRestart) + .filter((args) => args[0] === "observe" && historical.some((e) => e.job === args[1])), + ).toEqual([]); + // No extra rows were written to carry the retirement. + const bindingRows = yield* sql` + SELECT COUNT(*) AS n FROM projection_thread_activities WHERE kind = 'relay.binding' + `; + expect(Number((bindingRows[0] as { n: number }).n)).toBe(historical.length); + + // A worker this environment did project still reports its outage. + const liveJob = "job-aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa3"; + cli.setState({ + [liveJob]: { + schemaVersion: 1, + kind: "job", + id: liveJob, + attempt: 1, + sequence: 1, + status: "running", + providerType: "codex", + model: "gpt-6-sol", + effort: "high", + activity: { kind: "editing", label: "Editing files" }, + usage: { totalTokens: 4, inputTokens: 3, outputTokens: 1 }, + pending: false, + outcome: null, + }, + }); + yield* bridge.recordToolResult( + toolEvent({ + toolName: "mcp__relay__relay_delegate", + turnId: "turn-live", + itemId: "tool-live", + result: { structuredContent: { schemaVersion: 1, kind: "job", jobId: liveJob } }, + }), + ); + yield* bridge.reconcile; + const started = yield* sql` + SELECT payload_json AS payload FROM projection_thread_activities + WHERE kind = 'task.started' AND json_extract(payload_json, '$.taskId') = ${`relay:${liveJob}`} + `; + expect(started).toHaveLength(1); + + cli.setState({}); + yield* bridge.reconcile; + yield* bridge.reconcile; + yield* bridge.reconcile; + const outage = yield* sql` + SELECT json_extract(payload_json, '$.taskId') AS taskId + FROM projection_thread_activities + WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable' + `; + expect(outage).toEqual([{ taskId: `relay:${liveJob}` }]); + + // Reopening the thread after a restart must not republish the same + // history the previous boot already declined to announce. + const rebooted = yield* makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, recordingEngine(sql)), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, makeLiveness()), + ); + yield* rebooted.reconcile; + yield* rebooted.reconcile; + yield* rebooted.reconcile; + yield* rebooted.reconcile; + const afterReboot = yield* sql` + SELECT DISTINCT json_extract(payload_json, '$.taskId') AS taskId + FROM projection_thread_activities + WHERE kind = 'task.progress' AND summary = 'Relay observer unavailable' + `; + expect(afterReboot).toEqual([{ taskId: `relay:${liveJob}` }]); + }).pipe(Effect.ensuring(Effect.sync(() => cli.cleanup()))); + }); +}); + +persistence("Relay retirement reversal", (it) => { + it.effect("wakes a retired worker for a resume accepted after it was retired", () => { + const cli = fakeRelayCli(); + const revived = "job-bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbb1"; + cli.setState({}); + return Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + const engine = recordingEngine(sql); + const boot = () => + makeWithCliPath(cli.path).pipe( + Effect.provideService(OrchestrationEngineService, engine), + Effect.provideService(ServerEnvironment, fakeEnvironment), + Effect.provideService(ThreadBackgroundLivenessService, makeLiveness()), + ); + + // Dispatch a worker, then let Relay lose it before anything projects. + const first = yield* boot(); + yield* first.recordToolResult( + toolEvent({ + toolName: "mcp__relay__relay_delegate", + turnId: "turn-1", + itemId: "tool-1", + result: { structuredContent: { schemaVersion: 1, kind: "job", jobId: revived } }, + }), + ); + yield* first.reconcile; + yield* first.reconcile; + yield* first.reconcile; + yield* first.reconcile; + const retired = yield* sql` + SELECT payload_json AS payload FROM projection_thread_activities + WHERE activity_id = ${`relay-binding:${revived}`} + `; + expect(decodeJson(retired[0]?.payload)).toMatchObject({ id: revived }); + expect( + (decodeJson(retired[0]?.payload) as { retiredAfterRowId?: number }).retiredAfterRowId, + ).toBeGreaterThan(0); + + // A restart leaves it asleep. + const asleep = yield* boot(); + const beforeRestart = cli.calls().length; + yield* asleep.reconcile; + yield* asleep.reconcile; + expect( + cli + .calls() + .slice(beforeRestart) + .filter((args) => args[0] === "observe" && args[1] === revived), + ).toEqual([]); + + // A resume recorded after the retirement outranks it. + cli.setState({ + [revived]: { + schemaVersion: 1, + kind: "job", + id: revived, + attempt: 2, + sequence: 1, + status: "running", + providerType: "codex", + model: "gpt-6-sol", + effort: "high", + pending: false, + outcome: null, + }, + }); + yield* asleep.recordToolResult( + toolEvent({ + toolName: "mcp__relay__relay_resume", + turnId: "turn-2", + itemId: "tool-2", + result: { + structuredContent: { schemaVersion: 1, kind: "job", jobId: revived, attempt: 2 }, + }, + }), + ); + const awake = yield* boot(); + const beforeWake = cli.calls().length; + yield* awake.reconcile; + expect( + cli + .calls() + .slice(beforeWake) + .some((args) => args[0] === "observe" && args[1] === revived), + ).toBe(true); + const started = yield* sql` + SELECT payload_json AS payload FROM projection_thread_activities + WHERE kind = 'task.started' AND json_extract(payload_json, '$.taskId') = ${`relay:${revived}`} + `; + expect(started).toHaveLength(1); + }).pipe(Effect.ensuring(Effect.sync(() => cli.cleanup()))); + }); +}); diff --git a/apps/server/src/orchestration/RelayWorkerBridge.ts b/apps/server/src/orchestration/RelayWorkerBridge.ts index 1671b3fea6..2ba4f53f3a 100644 --- a/apps/server/src/orchestration/RelayWorkerBridge.ts +++ b/apps/server/src/orchestration/RelayWorkerBridge.ts @@ -52,6 +52,15 @@ interface RelayBinding { readonly panelId?: string; readonly agentIndex?: number; readonly slotAttempt?: number; + /** + * Watermark written once Relay itself reported this job gone while nothing + * about it had ever reached the thread. Boot skips a retired binding unless + * an activation was recorded after this point, so adopted history stops + * costing a probe on every start. A row id rather than a timestamp: the + * rest of this file already orders recovery that way, and same-millisecond + * writes are common. + */ + readonly retiredAfterRowId?: number; } interface RelayToolReceipt { @@ -489,6 +498,19 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu >(); const legacyPriorJobSlots = new Map>(); + /** Drop every trace of a binding this process has stopped observing. */ + const retire = (id: string): void => { + active.delete(id); + legacyPriorJobSlots.delete(id); + seen.delete(id); + failedObservations.delete(id); + missingObservationRetryAt.delete(id); + unavailable.delete(id); + outageEpochs.delete(id); + pendingPanelDispatchPolls.delete(id); + priorUsageCache.delete(id); + }; + const append = Effect.fn("RelayWorkerBridge.append")(function* ( binding: RelayBinding, kind: string, @@ -561,6 +583,9 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu ...(nonNegativeInteger(payload.slotAttempt) !== undefined ? { slotAttempt: payload.slotAttempt as number } : {}), + ...(nonNegativeInteger(payload.retiredAfterRowId) !== undefined + ? { retiredAfterRowId: payload.retiredAfterRowId as number } + : {}), }; }; @@ -917,10 +942,88 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu }; }); + const hasProjectedWorkerActivity = Effect.fn("RelayWorkerBridge.hasProjectedWorkerActivity")( + function* (binding: RelayBinding) { + const rows = yield* sql` + SELECT 1 FROM projection_thread_activities + WHERE thread_id = ${binding.threadId} AND kind LIKE 'task.%' + AND activity_id NOT LIKE 'relay-observer-unavailable:%' + AND CASE WHEN json_valid(payload_json) THEN json_extract(payload_json, '$.taskId') END = ${bindingTaskId(binding)} + LIMIT 1 + `; + return rows.length > 0; + }, + ); + + /** An activation for this binding (or its panel) recorded after `afterRowId`. */ + const hasActivationSince = Effect.fn("RelayWorkerBridge.hasActivationSince")(function* ( + binding: RelayBinding, + afterRowId: number, + ) { + const rows = yield* sql` + SELECT 1 FROM projection_thread_activities + WHERE thread_id = ${binding.threadId} AND kind = 'relay.activation' + AND rowid > ${afterRowId} + AND json_extract(payload_json, '$.environmentId') = ${environmentId} + AND ( + json_extract(payload_json, '$.id') = ${binding.id} + OR ( + json_extract(payload_json, '$.id') = ${binding.panelId ?? ""} + AND EXISTS ( + SELECT 1 FROM json_each(json_extract(payload_json, '$.jobIds')) + WHERE value = ${binding.id} + ) + ) + ) + LIMIT 1 + `; + return rows.length > 0; + }); + + /** + * Record that Relay reported this job gone while nothing about it had ever + * reached the thread. The binding row is upserted in place, so this costs no + * new activity and stays invisible to the transcript; boot then skips the + * binding instead of re-probing every job the environment has ever run. + */ + const markRetired = Effect.fn("RelayWorkerBridge.markRetired")(function* (binding: RelayBinding) { + const watermark = yield* sql` + SELECT MAX(rowid) AS rowId FROM projection_thread_activities + WHERE thread_id = ${binding.threadId} + `; + const retiredAfterRowId = nonNegativeInteger(record(watermark[0])?.rowId); + if (retiredAfterRowId === undefined) return; + yield* append( + binding, + "relay.binding", + { + kind: binding.kind, + id: binding.id, + toolCallId: binding.toolCallId, + environmentId: binding.environmentId, + ...(binding.panelId ? { panelId: binding.panelId } : {}), + ...(binding.agentIndex !== undefined ? { agentIndex: binding.agentIndex } : {}), + ...(binding.slotAttempt !== undefined ? { slotAttempt: binding.slotAttempt } : {}), + retiredAfterRowId, + }, + "Relay worker bound", + `relay-binding:${binding.id}`, + `retired:${retiredAfterRowId}`, + ); + }); + const shouldObserveOnBoot = Effect.fn("RelayWorkerBridge.shouldObserveOnBoot")(function* ( binding: RelayBinding, bindingRowId: number, ) { + // A retired binding stays asleep unless an activation arrived after it was + // retired. One that did wake still answers to the normal rules below, so a + // resumed worker that has since finished is not re-probed forever. + if ( + binding.retiredAfterRowId !== undefined && + !(yield* hasActivationSince(binding, binding.retiredAfterRowId)) + ) + return false; const latest = yield* readLatestTaskActivity(binding); if (latest.kind !== "task.completed" || bindingRowId > latest.rowId) return true; // A resume or panel continuation accepted after the terminal row must be @@ -1114,38 +1217,42 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu ) { const failures = (failedObservations.get(binding.id) ?? 0) + 1; failedObservations.set(binding.id, failures); - if (failures < 3 || unavailable.has(binding.id)) return; + if (failures < 3) return; + if (unavailable.has(binding.id)) { + // An announced outage is not re-announced, but the binding still owns a + // CLI spawn on every sweep until its recheck is pushed back out. + missingObservationRetryAt.set(binding.id, (yield* Clock.currentTimeMillis) + 60_000); + return; + } const taskId = bindingTaskId(binding); + // An outage row explains a worker the thread already shows. The startup + // receipt scan also adopts long-finished historical jobs whose Relay + // files are gone; those never projected a row, so announcing them turns + // every reopened thread into a wall of idle workers. + if (!(yield* hasProjectedWorkerActivity(binding))) { + // Relay itself answered that this job is gone, and nothing about it ever + // reached the thread, so there is no observation left to resume. Retire + // it for good: otherwise every job the environment has ever run is + // re-probed on each boot and holds a CLI spawn a minute in between. A + // worker that does come back re-registers through its own receipt, and + // its activation outranks the retirement. When Relay is absent entirely + // the silence says nothing about the job, so keep the binding live and + // only slow the recheck down. + if (enabled && cliPath) { + yield* markRetired(binding); + retire(binding.id); + } else { + unavailable.add(binding.id); + missingObservationRetryAt.set(binding.id, (yield* Clock.currentTimeMillis) + 60_000); + } + return; + } const latest = yield* readLatestTaskActivity(binding); const payload = latest.payload; const completedAttempt = nonNegativeInteger(payload?.attempt); const completedSequence = nonNegativeInteger(payload?.relaySequence); if (completedAttempt === undefined || completedSequence === undefined) { - // A recovered receipt may outlive its Relay files. Retain the binding - // for a later return, but do not recheck every missing historical job - // on every sweep ahead of live workers. - if (!unavailable.has(binding.id)) { - yield* append( - binding, - "task.progress", - { - taskId, - title: binding.kind === "panel" ? "Relay panel" : "Relay worker", - attempt: 1, - relaySequence: 0, - outageEpoch: 1, - status: "idle", - summary: "Relay observer unavailable", - detail: "Relay observer unavailable", - ...bindingPayload(binding), - cancellable: false, - }, - "Relay observer unavailable", - `relay-observer-unavailable:${binding.id}:1`, - "1:0:1", - ); - unavailable.add(binding.id); - } + unavailable.add(binding.id); missingObservationRetryAt.set(binding.id, (yield* Clock.currentTimeMillis) + 60_000); return; } @@ -1155,13 +1262,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu : undefined; if (latest.kind === "task.completed") { if (!pending) { - active.delete(binding.id); - legacyPriorJobSlots.delete(binding.id); - seen.delete(binding.id); - failedObservations.delete(binding.id); - unavailable.delete(binding.id); - outageEpochs.delete(binding.id); - priorUsageCache.delete(binding.id); + retire(binding.id); return; } } @@ -1349,25 +1450,12 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu : `relay-panel-complete:${binding.id}:${attempt}`, fingerprint, ); - active.delete(binding.id); - legacyPriorJobSlots.delete(binding.id); - seen.delete(binding.id); - failedObservations.delete(binding.id); - unavailable.delete(binding.id); - outageEpochs.delete(binding.id); - pendingPanelDispatchPolls.delete(binding.id); - priorUsageCache.delete(binding.id); + retire(binding.id); } else if (!hasRunningMember && (!hasPendingDispatch || pendingPolls >= 3)) { // Failed/cancelled members and unstarted slots remain visible as an // idle panel. A new continuation or member resume receipt reactivates // observation; polling this unchanged snapshot forever is needless. - active.delete(binding.id); - legacyPriorJobSlots.delete(binding.id); - seen.delete(binding.id); - failedObservations.delete(binding.id); - unavailable.delete(binding.id); - outageEpochs.delete(binding.id); - pendingPanelDispatchPolls.delete(binding.id); + retire(binding.id); } return; } @@ -1487,12 +1575,7 @@ export const makeWithCliPath = Effect.fn("RelayWorkerBridge.makeWithCliPath")(fu `relay-complete:${binding.id}:${observation.attempt}`, fingerprint, ); - active.delete(binding.id); - seen.delete(binding.id); - failedObservations.delete(binding.id); - unavailable.delete(binding.id); - outageEpochs.delete(binding.id); - priorUsageCache.delete(binding.id); + retire(binding.id); } }); diff --git a/apps/server/src/persistence/Migrations.ts b/apps/server/src/persistence/Migrations.ts index a50bb20be3..4acb8638f1 100644 --- a/apps/server/src/persistence/Migrations.ts +++ b/apps/server/src/persistence/Migrations.ts @@ -73,6 +73,7 @@ import Migration0059 from "./Migrations/059_ProjectionThreadPullRequests.ts"; import Migration0060 from "./Migrations/060_ProjectionThreadCompactionQueue.ts"; import Migration0061 from "./Migrations/061_ProjectionThreadMessageContext.ts"; import Migration0062 from "./Migrations/062_RepairStoppedSessionRequests.ts"; +import Migration0063 from "./Migrations/063_ClearUnprojectedRelayOutages.ts"; /** * Migration loader with all migrations defined inline. * @@ -178,6 +179,7 @@ const migrationEntries = [ // Upstream 51 is already Pylon durable rollback; retain that lineage and append. [61, "ProjectionThreadMessageContext", Migration0061], [62, "RepairStoppedSessionRequests", Migration0062], + [63, "ClearUnprojectedRelayOutages", Migration0063], ] as const; export const migrationManifest = migrationEntries.map(([id, name]) => [id, name] as const); diff --git a/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.test.ts b/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.test.ts new file mode 100644 index 0000000000..8035888a52 --- /dev/null +++ b/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.test.ts @@ -0,0 +1,65 @@ +import { assert, it } from "@effect/vitest"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as SqlClient from "effect/unstable/sql/SqlClient"; +import * as NodeSqliteClient from "@t3tools/shared/nodeSqliteClient"; + +import { runMigrations } from "../Migrations.ts"; + +it.layer(Layer.mergeAll(NodeSqliteClient.layerMemory()))( + "063_ClearUnprojectedRelayOutages", + (it) => { + it.effect("drops outages for workers a thread never started and keeps the rest", () => + Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + yield* runMigrations({ toMigrationInclusive: 62 }); + + yield* sql` + INSERT INTO projection_thread_activities ( + activity_id, thread_id, tone, kind, summary, payload_json, sequence, created_at + ) VALUES + ('relay-observer-unavailable:job-historical:1', 'thread-a', 'info', 'task.progress', + 'Relay observer unavailable', + '{"taskId":"relay:job-historical","status":"idle","summary":"Relay observer unavailable"}', + 1, '2026-09-24T21:16:16.382Z'), + ('relay-start:job-live:1', 'thread-a', 'info', 'task.started', + 'Relay worker started', '{"taskId":"relay:job-live","attempt":1}', + 2, '2026-09-24T20:00:00.000Z'), + ('relay-observer-unavailable:job-live:1', 'thread-a', 'info', 'task.progress', + 'Relay observer unavailable', + '{"taskId":"relay:job-live","status":"idle","summary":"Relay observer unavailable"}', + 3, '2026-09-24T21:16:16.400Z'), + ('relay-progress:job-historical', 'thread-a', 'info', 'task.progress', + 'Relay worker', '{"taskId":"relay:job-historical","status":"running"}', + 4, '2026-09-24T21:16:16.500Z'), + ('relay-observer-unavailable:job-other-thread:1', 'thread-b', 'info', 'task.progress', + 'Relay observer unavailable', + '{"taskId":"relay:job-other-thread","status":"idle"}', + 5, '2026-09-24T21:16:16.600Z'), + ('relay-start:job-other-thread:1', 'thread-c', 'info', 'task.started', + 'Relay worker started', '{"taskId":"relay:job-other-thread","attempt":1}', + 6, '2026-09-24T20:00:00.000Z') + `; + + yield* runMigrations({ toMigrationInclusive: 63 }); + + const remaining = yield* sql` + SELECT activity_id AS activityId FROM projection_thread_activities + ORDER BY activity_id + `; + assert.deepStrictEqual( + remaining.map((row) => (row as { activityId: string }).activityId), + [ + // A worker with any projected activity keeps its outage, even if + // an older provider never emitted its task.started row. + "relay-observer-unavailable:job-historical:1", + "relay-observer-unavailable:job-live:1", + "relay-progress:job-historical", + "relay-start:job-live:1", + "relay-start:job-other-thread:1", + ], + ); + }), + ); + }, +); diff --git a/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.ts b/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.ts new file mode 100644 index 0000000000..9167cf2b5b --- /dev/null +++ b/apps/server/src/persistence/Migrations/063_ClearUnprojectedRelayOutages.ts @@ -0,0 +1,28 @@ +import * as Effect from "effect/Effect"; +import * as SqlClient from "effect/unstable/sql/SqlClient"; + +// The Relay worker bridge adopts every persisted Relay receipt on boot, so its +// first run bound each historical job an environment had ever dispatched. Those +// jobs were long gone, and the failed observation published an idle outage row +// into the turn that had launched them -- reopening a thread showed a wall of +// stale workers. The bridge no longer announces a worker it never started; +// clear the rows it already wrote without rewriting the event history. +export default Effect.gen(function* () { + const sql = yield* SqlClient.SqlClient; + + yield* sql` + DELETE FROM projection_thread_activities AS outage + WHERE outage.kind = 'task.progress' + AND outage.activity_id LIKE 'relay-observer-unavailable:%' + AND NOT EXISTS ( + SELECT 1 FROM projection_thread_activities AS worker + WHERE worker.thread_id = outage.thread_id + AND worker.kind LIKE 'task.%' + AND worker.activity_id NOT LIKE 'relay-observer-unavailable:%' + AND CASE WHEN json_valid(worker.payload_json) + THEN json_extract(worker.payload_json, '$.taskId') END + = CASE WHEN json_valid(outage.payload_json) + THEN json_extract(outage.payload_json, '$.taskId') END + ) + `; +}); From 9611fac266e67277de933439ddd2a896346b74f7 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:42:15 -0600 Subject: [PATCH 090/104] fix(mobile): keep ordinary offline outbox retries out of warnings (#783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mobile): keep ordinary offline outbox retries out of warnings * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record p… * feat(web): filter sidebar from thread menu (#821) * feat(web): add bounded route history shortcuts on desktop (#803) * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(… * fix(web): add local dropped folders as path chips (#818) * feat(mobile): search projects when starting a task (#796) * feat(mobile): favorite models in thread settings (#790) * test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- .../state/thread-outbox-failure-log.test.ts | 147 ++++++++++++++++++ .../src/state/thread-outbox-failure-log.ts | 104 +++++++++++++ .../src/state/use-thread-outbox-drain.ts | 35 ++--- 3 files changed, 266 insertions(+), 20 deletions(-) create mode 100644 apps/mobile/src/state/thread-outbox-failure-log.test.ts create mode 100644 apps/mobile/src/state/thread-outbox-failure-log.ts diff --git a/apps/mobile/src/state/thread-outbox-failure-log.test.ts b/apps/mobile/src/state/thread-outbox-failure-log.test.ts new file mode 100644 index 0000000000..ef2727d48f --- /dev/null +++ b/apps/mobile/src/state/thread-outbox-failure-log.test.ts @@ -0,0 +1,147 @@ +import { + EnvironmentId, + MessageId, + OrchestrationDispatchCommandError, + ThreadId, +} from "@t3tools/contracts"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; + +import { + reportOutboxDeliveryFailure, + reportOutboxEditedAfterDelivery, + reportOutboxUploadFailure, +} from "./thread-outbox-failure-log"; + +const message = { + environmentId: EnvironmentId.make("environment-1"), + threadId: ThreadId.make("thread-1"), + messageId: MessageId.make("message-1"), +}; + +const debugFlag = globalThis as { __PYLON_OUTBOX_DEBUG__?: boolean }; +let warn: ReturnType; +let log: ReturnType; + +beforeEach(() => { + warn = vi.spyOn(console, "warn").mockImplementation(() => undefined); + log = vi.spyOn(console, "log").mockImplementation(() => undefined); + delete debugFlag.__PYLON_OUTBOX_DEBUG__; +}); + +afterEach(() => { + delete debugFlag.__PYLON_OUTBOX_DEBUG__; + vi.restoreAllMocks(); +}); + +describe("thread outbox failure logging", () => { + it("keeps an ordinary offline retry out of warnings while retaining opt-in diagnostics", () => { + const input = { + stage: "start-turn" as const, + error: new Error("Socket is not connected"), + interrupted: false, + context: message, + }; + expect(reportOutboxDeliveryFailure(input)).toBe("retry"); + expect(warn).not.toHaveBeenCalled(); + expect(log).not.toHaveBeenCalled(); + + expect( + reportOutboxDeliveryFailure({ + ...input, + error: { _tag: "RpcClientError", reason: { _tag: "SocketReadError" } }, + }), + ).toBe("retry"); + expect(warn).not.toHaveBeenCalled(); + + debugFlag.__PYLON_OUTBOX_DEBUG__ = true; + expect(reportOutboxDeliveryFailure(input)).toBe("retry"); + expect(log).toHaveBeenCalledWith( + "[pylon-thread-outbox] queued message delivery failed; retrying", + expect.objectContaining({ stage: "start-turn", action: "retry" }), + ); + expect(warn).not.toHaveBeenCalled(); + }); + + it("keeps server decisions and non-socket RPC failures visible as warnings", () => { + expect( + reportOutboxDeliveryFailure({ + stage: "start-turn", + error: new OrchestrationDispatchCommandError({ message: "Rejected" }), + interrupted: false, + context: message, + }), + ).toBe("hold"); + expect(warn).toHaveBeenCalledTimes(1); + + const decodeDefect = { _tag: "RpcClientError", reason: { _tag: "RpcClientDefect" } }; + expect( + reportOutboxDeliveryFailure({ + stage: "settings-sync", + error: decodeDefect, + interrupted: false, + context: message, + }), + ).toBe("retry"); + expect(warn).toHaveBeenCalledTimes(2); + + for (const reasonTag of ["StatusCodeError", "DecodeError", "WorkerError"]) { + expect( + reportOutboxDeliveryFailure({ + stage: "start-turn", + error: { _tag: "RpcClientError", reason: { _tag: reasonTag } }, + interrupted: false, + context: message, + }), + ).toBe("retry"); + } + expect(warn).toHaveBeenCalledTimes(5); + + expect( + reportOutboxDeliveryFailure({ + stage: "start-turn", + error: new Error("RpcClientError: StatusCodeError"), + interrupted: false, + context: message, + }), + ).toBe("retry"); + expect(warn).toHaveBeenCalledTimes(6); + + expect( + reportOutboxDeliveryFailure({ + stage: "start-turn", + error: new Error("Unexpected failure"), + interrupted: false, + context: message, + }), + ).toBe("hold"); + expect(warn).toHaveBeenCalledTimes(7); + }); + + it("treats interrupted requests and retryable uploads as ordinary", () => { + expect( + reportOutboxDeliveryFailure({ + stage: "start-turn", + error: new Error("Interrupted"), + interrupted: true, + context: message, + }), + ).toBe("retry"); + reportOutboxUploadFailure(message, new Error("Socket is not connected")); + expect(warn).not.toHaveBeenCalled(); + + reportOutboxUploadFailure(message, new Error("Invalid attachment")); + expect(warn).toHaveBeenCalledOnce(); + + reportOutboxUploadFailure(message, { + _tag: "RpcClientError", + reason: { _tag: "StatusCodeError" }, + }); + expect(warn).toHaveBeenCalledTimes(2); + }); + + it("does not warn when a user edit wins the delivery cleanup race", () => { + reportOutboxEditedAfterDelivery(message); + expect(warn).not.toHaveBeenCalled(); + expect(log).not.toHaveBeenCalled(); + }); +}); diff --git a/apps/mobile/src/state/thread-outbox-failure-log.ts b/apps/mobile/src/state/thread-outbox-failure-log.ts new file mode 100644 index 0000000000..092191c372 --- /dev/null +++ b/apps/mobile/src/state/thread-outbox-failure-log.ts @@ -0,0 +1,104 @@ +import { + resolveThreadOutboxFailureAction, + shouldRetryThreadOutboxDelivery, + type QueuedThreadMessage, + type ThreadOutboxCommandStage, + type ThreadOutboxFailureAction, +} from "./thread-outbox-model"; + +// The UI's transport-message helper also matches a bare "RpcClientError". For +// logging, require an actual connection symptom so other RPC failures stay visible. +const ORDINARY_CONNECTION_MESSAGES = [ + /\bSocket(?:Read|Write|Open|Close)Error\b/i, + /\bSocket is not connected\b/i, + /Unable to connect to the T3 server WebSocket\./i, + /\bis not connected\.$/i, + /\bdisconnected\.$/i, + /\bcould not establish a WebSocket connection\.$/i, + /\bping timeout\b/i, +] as const; + +function isOrdinaryTransportFailure(error: unknown): boolean { + if (typeof error === "object" && error !== null && "_tag" in error) { + if (error._tag === "RpcClientError") { + const reason: unknown = (error as { readonly reason?: unknown }).reason; + if (typeof reason !== "object" || reason === null || !("_tag" in reason)) return false; + return [ + "SocketReadError", + "SocketWriteError", + "SocketOpenError", + "SocketCloseError", + ].includes(String(reason._tag)); + } + return [ + "ConnectionTransientError", + "EnvironmentRpcUnavailableError", + "EnvironmentNotRegisteredError", + ].includes(String(error._tag)); + } + const message = + error instanceof Error + ? error.message + : typeof error === "string" + ? error + : typeof error === "object" && + error !== null && + "message" in error && + typeof error.message === "string" + ? error.message + : null; + return message !== null && ORDINARY_CONNECTION_MESSAGES.some((pattern) => pattern.test(message)); +} + +function debugLog(event: string, details: Record): void { + // Queued sends naturally retry offline. Keep those diagnostics available + // without writing a warning for every backoff attempt. + if ( + (globalThis as { readonly __PYLON_OUTBOX_DEBUG__?: boolean }).__PYLON_OUTBOX_DEBUG__ === true + ) { + console.log(`[pylon-thread-outbox] ${event}`, details); + } +} + +export function reportOutboxDeliveryFailure(input: { + readonly stage: ThreadOutboxCommandStage; + readonly error: unknown; + readonly interrupted: boolean; + readonly context: Record; +}): ThreadOutboxFailureAction { + const action = resolveThreadOutboxFailureAction(input); + const details = { ...input.context, stage: input.stage, action }; + if (action === "retry" && (input.interrupted || isOrdinaryTransportFailure(input.error))) { + debugLog("queued message delivery failed; retrying", details); + } else { + console.warn("[thread-outbox] queued message delivery failed", details); + } + return action; +} + +export function reportOutboxUploadFailure( + message: Pick, + error: unknown, +): void { + const details = { + environmentId: message.environmentId, + threadId: message.threadId, + messageId: message.messageId, + error, + }; + if (shouldRetryThreadOutboxDelivery(error) && isOrdinaryTransportFailure(error)) { + debugLog("attachment upload failed; retrying", details); + } else { + console.warn("[thread-outbox] failed to upload attachments", details); + } +} + +export function reportOutboxEditedAfterDelivery( + message: Pick, +): void { + debugLog("delivered message was edited before cleanup", { + environmentId: message.environmentId, + threadId: message.threadId, + messageId: message.messageId, + }); +} diff --git a/apps/mobile/src/state/use-thread-outbox-drain.ts b/apps/mobile/src/state/use-thread-outbox-drain.ts index 914f525deb..f9e245de57 100644 --- a/apps/mobile/src/state/use-thread-outbox-drain.ts +++ b/apps/mobile/src/state/use-thread-outbox-drain.ts @@ -22,6 +22,11 @@ import { shouldRefreshProviderModelCatalog, } from "../lib/providerModelSelection"; import { createQueuedModelCatalogRefresh } from "./queued-model-catalog-refresh"; +import { + reportOutboxDeliveryFailure, + reportOutboxEditedAfterDelivery, + reportOutboxUploadFailure, +} from "./thread-outbox-failure-log"; import { scopedThreadKey } from "../lib/scopedEntities"; import { buildProjectThreadStartTurnInput } from "../lib/projectThreadStartTurn"; @@ -53,7 +58,6 @@ import { resolveConfirmedThreadOutboxPlan, resolveThreadOutboxDeliveryAction, resolveThreadOutboxDispatchStep, - resolveThreadOutboxFailureAction, shouldRetryThreadOutboxDelivery, threadOutboxDeliveryHoldsEqual, sourceEpochMismatchHold, @@ -237,14 +241,7 @@ export async function completeQueuedMessageDelivery( ); if (!removed) { forgetAcknowledgedThreadMessage(queuedMessage); - console.warn( - "[thread-outbox] delivered message was edited before cleanup; keeping the newer message", - { - environmentId: queuedMessage.environmentId, - threadId: queuedMessage.threadId, - messageId: queuedMessage.messageId, - }, - ); + reportOutboxEditedAfterDelivery(queuedMessage); return "edited"; } return "removed"; @@ -677,18 +674,16 @@ export function useThreadOutboxDrain(): void { return null; } const error = Cause.squash(commandResult.cause); - const action = resolveThreadOutboxFailureAction({ + const action = reportOutboxDeliveryFailure({ stage, error, interrupted: Cause.hasInterruptsOnly(commandResult.cause), - }); - console.warn("[thread-outbox] queued message delivery failed", { - environmentId: queuedMessage.environmentId, - threadId: queuedMessage.threadId, - messageId: queuedMessage.messageId, - stage, - cause: commandResult.cause, - action, + context: { + environmentId: queuedMessage.environmentId, + threadId: queuedMessage.threadId, + messageId: queuedMessage.messageId, + cause: commandResult.cause, + }, }); const epochHold = stage === "start-turn" ? sourceEpochMismatchHold(error) : null; return { @@ -764,7 +759,7 @@ export function useThreadOutboxDrain(): void { return "complete"; } } catch (error) { - console.warn("[thread-outbox] failed to upload attachments", error); + reportOutboxUploadFailure(queuedMessage, error); if (!shouldRetryThreadOutboxDelivery(error)) { const restored = await restoreQueuedMessage( queuedMessage, @@ -882,7 +877,7 @@ export function useThreadOutboxDrain(): void { return "complete"; } } catch (error) { - console.warn("[thread-outbox] failed to upload attachments", error); + reportOutboxUploadFailure(queuedMessage, error); if (!shouldRetryThreadOutboxDelivery(error)) { const restored = await restoreQueuedMessage( queuedMessage, From b34fe34cff52758392acc43e9f689c05efc0bdb9 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:42:55 -0600 Subject: [PATCH 091/104] fix(web): respect panel motion and nested timeline scrolling (#750) Adapt T3 Code sources da6a85b1365993d0ff2a79698cd82498de247d8a, 7c2702d68ae2e10fa9d3a08c996ed47c393dcffd, and 68607c5a99147710b92228e0642f6c0a642e32a6 for Pylon composer and context controls. --- .../web/src/components/ChatView.logic.test.ts | 131 +--------------- apps/web/src/components/ChatView.logic.ts | 16 -- apps/web/src/components/ChatView.tsx | 59 +++++--- apps/web/src/components/chat/ChatComposer.tsx | 31 ++-- .../chat/draftHeroTransition.test.ts | 35 ++++- .../components/chat/draftHeroTransition.ts | 17 ++- .../chat/timelineScrollTarget.test.ts | 142 ++++++++++++++++++ .../components/chat/timelineScrollTarget.ts | 31 ++++ apps/web/src/index.css | 11 +- 9 files changed, 282 insertions(+), 191 deletions(-) create mode 100644 apps/web/src/components/chat/timelineScrollTarget.test.ts create mode 100644 apps/web/src/components/chat/timelineScrollTarget.ts diff --git a/apps/web/src/components/ChatView.logic.test.ts b/apps/web/src/components/ChatView.logic.test.ts index 025a9c2648..0e4b7920f9 100644 --- a/apps/web/src/components/ChatView.logic.test.ts +++ b/apps/web/src/components/ChatView.logic.test.ts @@ -14,7 +14,7 @@ import { type ServerProvider, type WorktreeSetupSnapshot, } from "@t3tools/contracts"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; +import { afterEach, describe, expect, it, vi } from "vite-plus/test"; import { Atom, AsyncResult } from "effect/unstable/reactivity"; import { appAtomRegistry } from "../rpc/atomRegistry"; import { environmentThreadDetails } from "../state/threads"; @@ -91,7 +91,6 @@ import { observeProactivePanelUserChoice, shouldShowPlanFollowUpPrompt, shouldWriteThreadErrorToCurrentServerThread, - toolGroupConsumesUpwardNavigation, getAntigravitySendBlockReason, waitForRevertedMessage, prepareRevertedMessageAttachments, @@ -418,134 +417,6 @@ describe("proactive panels", () => { }); }); -describe("toolGroupConsumesUpwardNavigation", () => { - class ScrollElement extends EventTarget { - scrollTop = 0; - scrollHeight = 100; - clientHeight = 100; - overflowY = "visible"; - - constructor( - readonly parentElement: ScrollElement | null = null, - readonly isToolGroup = false, - ) { - super(); - } - - closest(selector: string): ScrollElement | null { - if (selector !== "[data-tool-group-scroll]") return null; - return this.isToolGroup ? this : (this.parentElement?.closest(selector) ?? null); - } - } - - beforeEach(() => { - vi.stubGlobal("Element", ScrollElement); - vi.stubGlobal("getComputedStyle", (element: ScrollElement) => ({ - overflowY: element.overflowY, - })); - }); - afterEach(() => vi.unstubAllGlobals()); - - it("releases upward navigation when an overflowing group is at the top", () => { - const group = Object.assign(new ScrollElement(null, true), { - overflowY: "auto", - scrollHeight: 300, - }); - - expect(toolGroupConsumesUpwardNavigation(new ScrollElement(group))).toBe(false); - }); - - it.each([ - { overflowY: "auto", scrollTop: 1 }, - { overflowY: "auto", scrollTop: 0.25 }, - { overflowY: "scroll", scrollTop: 80 }, - ])("consumes upward navigation within a scrolled group: %j", (scroll) => { - const group = Object.assign(new ScrollElement(null, true), { - scrollHeight: 300, - ...scroll, - }); - - expect(toolGroupConsumesUpwardNavigation(group)).toBe(true); - }); - - it.each([100, 300])( - "consumes scrolling in a nested result with a group content height of %i", - (scrollHeight) => { - const group = Object.assign(new ScrollElement(null, true), { - overflowY: "auto", - scrollHeight, - }); - const result = Object.assign(new ScrollElement(group), { - overflowY: "auto", - scrollHeight: 300, - scrollTop: 0.25, - }); - - expect(toolGroupConsumesUpwardNavigation(new ScrollElement(result))).toBe(true); - }, - ); - - it("releases upward navigation when the group and nested result are both at the top", () => { - const group = Object.assign(new ScrollElement(null, true), { - overflowY: "auto", - scrollHeight: 300, - }); - const result = Object.assign(new ScrollElement(group), { - overflowY: "scroll", - scrollHeight: 300, - }); - - expect(toolGroupConsumesUpwardNavigation(new ScrollElement(result))).toBe(false); - }); - - it("ignores targets outside a tool group and non-element targets", () => { - const outside = Object.assign(new ScrollElement(), { - overflowY: "auto", - scrollHeight: 300, - scrollTop: 40, - }); - - expect(toolGroupConsumesUpwardNavigation(outside)).toBe(false); - expect(toolGroupConsumesUpwardNavigation(new EventTarget())).toBe(false); - expect(toolGroupConsumesUpwardNavigation(null)).toBe(false); - }); - - it("does not consume scrolling from an ancestor beyond the tool group", () => { - const timeline = Object.assign(new ScrollElement(), { - overflowY: "auto", - scrollHeight: 300, - scrollTop: 40, - }); - const group = new ScrollElement(timeline, true); - - expect(toolGroupConsumesUpwardNavigation(new ScrollElement(group))).toBe(false); - }); - - it.each(["hidden", "clip", "visible"])( - "ignores a non-scrollable child with overflow-y %s", - (overflowY) => { - const group = new ScrollElement(null, true); - const result = Object.assign(new ScrollElement(group), { - overflowY, - scrollHeight: 300, - scrollTop: 40, - }); - - expect(toolGroupConsumesUpwardNavigation(new ScrollElement(result))).toBe(false); - }, - ); - - it("does not consume programmatic scrolling on an overflow-hidden group", () => { - const group = Object.assign(new ScrollElement(null, true), { - overflowY: "hidden", - scrollHeight: 300, - scrollTop: 40, - }); - - expect(toolGroupConsumesUpwardNavigation(group)).toBe(false); - }); -}); - const environmentId = EnvironmentId.make("environment-local"); const projectId = ProjectId.make("project-1"); const threadId = ThreadId.make("thread-1"); diff --git a/apps/web/src/components/ChatView.logic.ts b/apps/web/src/components/ChatView.logic.ts index 1b0ba04ce6..7885042bdc 100644 --- a/apps/web/src/components/ChatView.logic.ts +++ b/apps/web/src/components/ChatView.logic.ts @@ -225,22 +225,6 @@ export function shouldReleaseTimelineAnchorForToolActivity(input: { }); } -export function toolGroupConsumesUpwardNavigation(target: EventTarget | null): boolean { - const elementTarget = target instanceof Element ? target : null; - const group = elementTarget?.closest("[data-tool-group-scroll]"); - if (!group) return false; - - // A nested result or the group itself can consume an upward scroll. - for (let element = elementTarget; element; element = element.parentElement) { - if (element.scrollTop > 0) { - const overflowY = getComputedStyle(element).overflowY; - if (overflowY === "auto" || overflowY === "scroll") return true; - } - if (element === group) break; - } - return false; -} - export { findRecordedWorktreeSetup, resolveVisibleWorktreeSetup, diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 666a362baa..848a28752f 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -403,6 +403,7 @@ import { import { environmentShell } from "../state/shell"; import { ChatComposer, type ChatComposerHandle } from "./chat/ChatComposer"; import { createPageScrollController, type PageScrollKey } from "./chat/pageScrollController"; +import { isTimelineScrollTarget } from "./chat/timelineScrollTarget"; import { DraftHeroHeadline } from "./chat/DraftHeroHeadline"; import { ExpandedImageDialog } from "./chat/ExpandedImageDialog"; import { PullRequestThreadDialog } from "./PullRequestThreadDialog"; @@ -463,7 +464,6 @@ import { import { deriveLatestContextWindowSnapshot, formatContextWindowTokens } from "../lib/contextWindow"; import { DRAFT_HERO_TRANSITION_ANIMATION_ID, - DRAFT_HERO_TRANSITION_DURATION_MS, DRAFT_HERO_TRANSITION_EASING, MOBILE_COMPOSER_VIEW_TRANSITION_NAME, MOBILE_DRAFT_HEADLINE_VIEW_TRANSITION_NAME, @@ -532,7 +532,6 @@ import { shouldWriteThreadErrorToCurrentServerThread, startNewThreadForProject, codexArtifactTemplatePromptToAppend, - toolGroupConsumesUpwardNavigation, waitForStartedServerThread, shouldRefocusComposerOnWindowFocus, } from "./ChatView.logic"; @@ -596,7 +595,11 @@ const EMPTY_ACTIVITIES: OrchestrationThreadActivity[] = []; const EMPTY_PROVIDERS: ServerProvider[] = []; const EMPTY_PROVIDER_SKILLS: ServerProvider["skills"] = []; const EMPTY_PENDING_USER_INPUT_ANSWERS: Record = {}; -function useDraftHeroLayoutTransition(isDraftHeroState: boolean) { +function useDraftHeroLayoutTransition( + isDraftHeroState: boolean, + animationsActive: boolean, + animationDurationMs: number, +) { const transitionGroupRef = useRef(null); const composerAnchorRef = useRef(null); const previousStateRef = useRef(isDraftHeroState); @@ -616,9 +619,6 @@ function useDraftHeroLayoutTransition(isDraftHeroState: boolean) { const transitionGroup = transitionGroupRef.current; const nextComposerRect = composerAnchorRef.current?.getBoundingClientRect() ?? null; const stateChanged = previousStateRef.current !== isDraftHeroState; - const prefersReducedMotion = - typeof window !== "undefined" && - window.matchMedia?.("(prefers-reduced-motion: reduce)").matches; const mobileComposerTransitionActive = typeof document !== "undefined" && document.documentElement.dataset.mobileComposerRouteTransition === "true"; @@ -629,7 +629,7 @@ function useDraftHeroLayoutTransition(isDraftHeroState: boolean) { const previousComposerRect = previousComposerRectRef.current; if ( stateChanged && - !prefersReducedMotion && + animationsActive && !mobileComposerTransitionActive && transitionGroup && previousComposerRect && @@ -645,7 +645,7 @@ function useDraftHeroLayoutTransition(isDraftHeroState: boolean) { { transform: "translate3d(0, 0, 0)" }, ], { - duration: DRAFT_HERO_TRANSITION_DURATION_MS, + duration: animationDurationMs, easing: DRAFT_HERO_TRANSITION_EASING, }, ); @@ -664,7 +664,7 @@ function useDraftHeroLayoutTransition(isDraftHeroState: boolean) { previousStateRef.current = isDraftHeroState; previousComposerRectRef.current = nextComposerRect; - }, [isDraftHeroState]); + }, [animationDurationMs, animationsActive, isDraftHeroState]); return [attachTransitionGroupRef, attachComposerAnchorRef, captureComposerRect] as const; } @@ -3868,7 +3868,11 @@ export default function ChatView(props: ChatViewProps) { attachDraftHeroTransitionGroupRef, attachDraftHeroComposerAnchorRef, captureDraftHeroComposerRect, - ] = useDraftHeroLayoutTransition(isDraftHeroState); + ] = useDraftHeroLayoutTransition( + isDraftHeroState, + panelAnimationsActive, + panelAnimationDurationMs, + ); const rollbackActive = isRollbackActive(activeThread?.rollbackStatus); const rollbackTargetIdle = activeThread?.session !== null && @@ -5765,6 +5769,8 @@ export default function ChatView(props: ChatViewProps) { // Only an upward wheel is a navigation intent; wheeling down while // following either does nothing (at the end) or moves toward it. const handleWheel = (event: WheelEvent) => { + if (event.ctrlKey || !isTimelineScrollTarget(event.target, scrollNode, event.deltaY)) + return; if (event.deltaY > 0) { timelineScrollIntentRef.current = "toward-end"; if (isAtEndRef.current) { @@ -5773,11 +5779,7 @@ export default function ChatView(props: ChatViewProps) { } else if (event.deltaY < 0) { timelineScrollIntentRef.current = "away-from-end"; } - if ( - event.deltaY < 0 && - contentScrollsUp() && - !toolGroupConsumesUpwardNavigation(event.target) - ) { + if (event.deltaY < 0 && contentScrollsUp()) { handleManualNavigation(); } }; @@ -5827,12 +5829,20 @@ export default function ChatView(props: ChatViewProps) { ) { return; } + if (!["PageUp", "Home", "ArrowUp", "PageDown", "End", "ArrowDown"].includes(event.key)) + return; + const scrollDirection = ["PageUp", "Home", "ArrowUp"].includes(event.key) ? -1 : 1; + if ( + scrollNode.contains(event.target) && + !isTimelineScrollTarget(event.target, scrollNode, scrollDirection) + ) + return; switch (event.key) { case "PageUp": case "Home": case "ArrowUp": timelineScrollIntentRef.current = "away-from-end"; - if (contentScrollsUp() && !toolGroupConsumesUpwardNavigation(event.target)) { + if (contentScrollsUp()) { handleManualNavigation(); composerRef.current?.collapseForTimelineScrollKey(event.key); } @@ -8202,13 +8212,16 @@ export default function ChatView(props: ChatViewProps) { const dockStarted = new Promise((resolve) => { resolveDockStarted = resolve; }); - const dockTransition = runMobileComposerTransition(() => { - flushSync(() => { - captureDraftHeroComposerRect(); - setDockedDraftHeroThreadKey(activeThreadKey); - }); - resolveDockStarted?.(); - }); + const dockTransition = runMobileComposerTransition( + () => { + flushSync(() => { + captureDraftHeroComposerRect(); + setDockedDraftHeroThreadKey(activeThreadKey); + }); + resolveDockStarted?.(); + }, + { active: panelAnimationsActive, durationMs: panelAnimationDurationMs }, + ); void dockTransition.catch(() => resolveDockStarted?.()); await dockStarted; } diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 3e094c2ad8..990a9525cb 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -356,6 +356,7 @@ import { import { ComposerPromptLengthValidation } from "./ComposerPromptLengthValidation"; import { PierreEntryIcon } from "./PierreEntryIcon"; import { pendingDraftWork } from "./pendingDraftWork"; +import { isTimelineScrollTarget } from "./timelineScrollTarget"; import { createComposerScrollGestureState, recordComposerScrollGestureEvent, @@ -441,7 +442,6 @@ const EMPTY_PULL_REQUEST_LIST_TARGETS: ReadonlyArray, onOverlayHeightChange: (height: number) => void, + animationsActive: boolean, + animationDurationMs: number, ) { const elementRef = useRef(null); const isCollapsedRef = useRef(isCollapsed); @@ -569,7 +571,6 @@ function useComposerRestingTransition( const previousHeight = interruptedHeight ?? previousHeightRef.current; const targetChanged = interruptedTargetHeight === null || Math.abs(interruptedTargetHeight - nextHeight) >= 0.5; - const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches; const shouldAnimate = shouldAnimateComposerRestingTransition({ hasCompletedInitialLayout: hasCompletedInitialLayoutRef.current, stateChanged, @@ -578,18 +579,16 @@ function useComposerRestingTransition( if ( shouldAnimate && - !prefersReducedMotion && + animationsActive && previousHeight !== null && Math.abs(previousHeight - nextHeight) >= 0.5 ) { const remainingDuration = typeof interruptedDuration === "number" && interruptedCurrentTime !== null ? Math.max(1, interruptedDuration - interruptedCurrentTime) - : COMPOSER_RESTING_TRANSITION_DURATION_MS; + : animationDurationMs; const duration = - interruptedHeight !== null && !targetChanged - ? remainingDuration - : COMPOSER_RESTING_TRANSITION_DURATION_MS; + interruptedHeight !== null && !targetChanged ? remainingDuration : animationDurationMs; element.style.overflow = "clip"; surface.style.height = "100%"; @@ -789,7 +788,13 @@ function useComposerRestingTransition( actionFromBottom: nextActionTop === null ? null : nextRect.bottom - nextActionTop, }; }, - [clearTransitionStyles, onOverlayHeightChange, restingControlsRef], + [ + animationDurationMs, + animationsActive, + clearTransitionStyles, + onOverlayHeightChange, + restingControlsRef, + ], ); useLayoutEffect(() => { @@ -5715,6 +5720,8 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) isComposerResting, restingComposerControlsRef, onComposerOverlayHeightChange, + panelAnimationsActive, + panelAnimationDurationMs, ); const canTrackComposerScrollGesture = routeKind === "server" && activeThreadId !== null && !isMobileViewport; @@ -5774,8 +5781,12 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) const scrollNode = getTimelineScrollableNode(); if (!scrollNode) return; - const targetsTimeline = scrollNode.contains(event.target); - if (!targetsTimeline && !composerScrollGestureRef.current.collapseSuppressed) return; + const targetsTimeline = isTimelineScrollTarget(event.target, scrollNode, event.deltaY); + if ( + !scrollNode.contains(event.target) && + !composerScrollGestureRef.current.collapseSuppressed + ) + return; if (composerScrollCollapseTimeoutRef.current !== null) { window.clearTimeout(composerScrollCollapseTimeoutRef.current); diff --git a/apps/web/src/components/chat/draftHeroTransition.test.ts b/apps/web/src/components/chat/draftHeroTransition.test.ts index c9d43d5415..d6adf79e5d 100644 --- a/apps/web/src/components/chat/draftHeroTransition.test.ts +++ b/apps/web/src/components/chat/draftHeroTransition.test.ts @@ -56,8 +56,9 @@ describe("runMobileComposerTransition", () => { finishTransition = resolve; }); const dataset: Record = {}; + const style = { setProperty: vi.fn(), removeProperty: vi.fn() }; vi.stubGlobal("document", { - documentElement: { dataset }, + documentElement: { dataset, style }, getAnimations: () => [], startViewTransition: (update: () => void | Promise) => { void update(); @@ -68,7 +69,10 @@ describe("runMobileComposerTransition", () => { matchMedia: (query: string) => ({ matches: query === "(max-width: 639px)" }), }); - const transition = runMobileComposerTransition(() => undefined); + const transition = runMobileComposerTransition(() => undefined, { + active: true, + durationMs: 240, + }); await Promise.resolve(); let handoffComplete = false; @@ -85,11 +89,12 @@ describe("runMobileComposerTransition", () => { it("uses a scoped view transition on mobile", async () => { const dataset: Record = {}; + const style = { setProperty: vi.fn(), removeProperty: vi.fn() }; const startViewTransition = vi.fn((update: () => void | Promise) => ({ finished: Promise.resolve(update()).then(() => undefined), })); vi.stubGlobal("document", { - documentElement: { dataset }, + documentElement: { dataset, style }, startViewTransition, }); vi.stubGlobal("window", { @@ -97,13 +102,33 @@ describe("runMobileComposerTransition", () => { }); const update = vi.fn(); - await runMobileComposerTransition(update); + await runMobileComposerTransition(update, { active: true, durationMs: 360 }); expect(startViewTransition).toHaveBeenCalledOnce(); expect(update).toHaveBeenCalledOnce(); + expect(style.setProperty).toHaveBeenCalledWith( + "--mobile-composer-transition-duration", + "360ms", + ); + expect(style.removeProperty).toHaveBeenCalledWith("--mobile-composer-transition-duration"); expect(dataset).not.toHaveProperty("mobileComposerRouteTransition"); }); + it("updates without a view transition when panel animations are inactive", async () => { + const startViewTransition = vi.fn(); + vi.stubGlobal("document", { + documentElement: { dataset: {} }, + startViewTransition, + }); + vi.stubGlobal("window", { matchMedia: () => ({ matches: true }) }); + const update = vi.fn(); + + await runMobileComposerTransition(update, { active: false, durationMs: 360 }); + + expect(startViewTransition).not.toHaveBeenCalled(); + expect(update).toHaveBeenCalledOnce(); + }); + it("updates without a view transition when reduced motion is preferred", async () => { const startViewTransition = vi.fn(); vi.stubGlobal("document", { @@ -115,7 +140,7 @@ describe("runMobileComposerTransition", () => { }); const update = vi.fn(); - await runMobileComposerTransition(update); + await runMobileComposerTransition(update, { active: true, durationMs: 360 }); expect(startViewTransition).not.toHaveBeenCalled(); expect(update).toHaveBeenCalledOnce(); diff --git a/apps/web/src/components/chat/draftHeroTransition.ts b/apps/web/src/components/chat/draftHeroTransition.ts index 0773407cfc..958b7374c1 100644 --- a/apps/web/src/components/chat/draftHeroTransition.ts +++ b/apps/web/src/components/chat/draftHeroTransition.ts @@ -1,8 +1,8 @@ export const DRAFT_HERO_TRANSITION_ANIMATION_ID = "t3-draft-hero-transition"; -export const DRAFT_HERO_TRANSITION_DURATION_MS = 180; export const DRAFT_HERO_TRANSITION_EASING = "cubic-bezier(0.4, 0, 0.2, 1)"; export const MOBILE_COMPOSER_VIEW_TRANSITION_NAME = "t3-mobile-composer"; export const MOBILE_DRAFT_HEADLINE_VIEW_TRANSITION_NAME = "t3-mobile-draft-headline"; +const MOBILE_COMPOSER_TRANSITION_DURATION_PROPERTY = "--mobile-composer-transition-duration"; type ComposerViewTransition = { readonly finished: Promise; @@ -39,6 +39,7 @@ export async function waitForDraftHeroTransition(): Promise { export async function runMobileComposerTransition( update: () => void | Promise, + options: { active: boolean; durationMs: number }, ): Promise { if (typeof document === "undefined" || typeof window === "undefined") { await update(); @@ -49,7 +50,12 @@ export async function runMobileComposerTransition( const mobileViewport = window.matchMedia?.("(max-width: 639px)").matches ?? false; const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false; - if (!mobileViewport || prefersReducedMotion || !transitionDocument.startViewTransition) { + if ( + !options.active || + !mobileViewport || + prefersReducedMotion || + !transitionDocument.startViewTransition + ) { await update(); return; } @@ -61,6 +67,10 @@ export async function runMobileComposerTransition( await update(); }; let transitionFinished: Promise | null = null; + transitionDocument.documentElement.style.setProperty( + MOBILE_COMPOSER_TRANSITION_DURATION_PROPERTY, + `${String(options.durationMs)}ms`, + ); transitionDocument.documentElement.dataset.mobileComposerRouteTransition = "true"; try { const transition = transitionDocument.startViewTransition(runUpdate); @@ -78,5 +88,8 @@ export async function runMobileComposerTransition( activeMobileComposerTransition = null; } delete transitionDocument.documentElement.dataset.mobileComposerRouteTransition; + transitionDocument.documentElement.style.removeProperty( + MOBILE_COMPOSER_TRANSITION_DURATION_PROPERTY, + ); } } diff --git a/apps/web/src/components/chat/timelineScrollTarget.test.ts b/apps/web/src/components/chat/timelineScrollTarget.test.ts new file mode 100644 index 0000000000..051f7785f5 --- /dev/null +++ b/apps/web/src/components/chat/timelineScrollTarget.test.ts @@ -0,0 +1,142 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; + +import { + createComposerScrollGestureState, + recordComposerScrollGestureEvent, +} from "./composerScrollGesture"; +import { isTimelineScrollTarget } from "./timelineScrollTarget"; + +class ScrollElement extends EventTarget { + scrollTop = 0; + scrollHeight = 100; + clientHeight = 100; + overflowY = "visible"; + overscrollBehaviorY = "auto"; + + constructor(readonly parentElement: ScrollElement | null = null) { + super(); + } + + contains(target: ScrollElement): boolean { + return ( + target === this || (target.parentElement !== null && this.contains(target.parentElement)) + ); + } +} + +function targetsTimeline(target: EventTarget | null, timeline: ScrollElement, deltaY: number) { + return isTimelineScrollTarget(target, timeline as unknown as HTMLElement, deltaY); +} + +function setup() { + const timeline = Object.assign(new ScrollElement(), { + overflowY: "auto", + scrollHeight: 1500, + clientHeight: 500, + scrollTop: 1000, + }); + const group = Object.assign(new ScrollElement(timeline), { + overflowY: "auto", + scrollHeight: 300, + scrollTop: 80, + }); + return { timeline, group, content: new ScrollElement(group) }; +} + +beforeEach(() => { + vi.stubGlobal("Element", ScrollElement); + vi.stubGlobal("getComputedStyle", (element: ScrollElement) => element); +}); +afterEach(() => vi.unstubAllGlobals()); + +describe("timeline scroll targets", () => { + it.each([-30, 30])("keeps a nested tool group's scroll out of the timeline: %i", (deltaY) => { + const { timeline, group, content } = setup(); + expect(targetsTimeline(content, timeline, deltaY)).toBe(false); + expect(targetsTimeline(group, timeline, deltaY)).toBe(false); + }); + + it.each([ + { scrollTop: 0, deltaY: -30 }, + { scrollTop: 200, deltaY: 30 }, + ])("allows chaining only past the matching edge: %j", ({ scrollTop, deltaY }) => { + const { timeline, group, content } = setup(); + group.scrollTop = scrollTop; + expect(targetsTimeline(content, timeline, deltaY)).toBe(true); + expect(targetsTimeline(content, timeline, -deltaY)).toBe(false); + }); + + it.each(["contain", "none"])("respects overscroll-y %s at either edge", (overscrollBehaviorY) => { + const { timeline, group, content } = setup(); + group.overscrollBehaviorY = overscrollBehaviorY; + group.scrollTop = 0; + expect(targetsTimeline(content, timeline, -30)).toBe(false); + group.scrollTop = 200; + expect(targetsTimeline(content, timeline, 30)).toBe(false); + group.scrollTop = 0; + group.scrollHeight = group.clientHeight; + expect(targetsTimeline(content, timeline, 30)).toBe(false); + }); + + it("checks nested results even when the tool group cannot scroll", () => { + const { timeline, group } = setup(); + group.scrollTop = 0; + group.scrollHeight = group.clientHeight; + const result = Object.assign(new ScrollElement(group), { + overflowY: "scroll", + scrollHeight: 300, + scrollTop: 0.25, + }); + expect(targetsTimeline(new ScrollElement(result), timeline, -30)).toBe(false); + result.scrollTop = 0; + expect(targetsTimeline(result, timeline, -30)).toBe(true); + }); + + it("checks an outer group when an inner result reaches its edge", () => { + const { timeline, group } = setup(); + const result = Object.assign(new ScrollElement(group), { overflowY: "auto" }); + expect(targetsTimeline(result, timeline, -30)).toBe(false); + group.scrollTop = 0; + expect(targetsTimeline(result, timeline, -30)).toBe(true); + }); + + it.each(["visible", "hidden", "clip"])("ignores overflow-y %s", (overflowY) => { + const { timeline, group, content } = setup(); + group.overflowY = overflowY; + expect(targetsTimeline(content, timeline, -30)).toBe(true); + }); + + it("allows ordinary message content and the outer viewport", () => { + const { timeline } = setup(); + expect(targetsTimeline(new ScrollElement(timeline), timeline, -30)).toBe(true); + expect(targetsTimeline(timeline, timeline, 30)).toBe(true); + }); + + it("rejects outside targets, non-elements, and horizontal-only scrolling", () => { + const { timeline, content } = setup(); + expect(targetsTimeline(new ScrollElement(), timeline, -30)).toBe(false); + expect(targetsTimeline(new EventTarget(), timeline, -30)).toBe(false); + expect(targetsTimeline(null, timeline, -30)).toBe(false); + expect(targetsTimeline(content, timeline, 0)).toBe(false); + }); + + it("does not accumulate nested scrolling toward composer collapse", () => { + const { timeline, group, content } = setup(); + const state = createComposerScrollGestureState(); + const record = (target: ScrollElement, now: number, deltaPx: number) => + recordComposerScrollGestureEvent(state, { + now, + deltaPx, + collapseThresholdPx: 24, + collapseEligible: targetsTimeline(target, timeline, -deltaPx), + canScrollInGestureDirection: timeline.scrollTop > 0, + scrollsTowardLogicalEnd: false, + }); + + expect(record(timeline, 0, 20)).toBe(false); + expect(record(content, 20, 30)).toBe(false); + group.scrollTop = 0; + expect(record(content, 40, 10)).toBe(false); + expect(record(content, 60, 14)).toBe(true); + }); +}); diff --git a/apps/web/src/components/chat/timelineScrollTarget.ts b/apps/web/src/components/chat/timelineScrollTarget.ts new file mode 100644 index 0000000000..da87e9a703 --- /dev/null +++ b/apps/web/src/components/chat/timelineScrollTarget.ts @@ -0,0 +1,31 @@ +// A gesture inside the timeline may belong to a nested tool result or code +// block. Only treat it as timeline navigation if it can chain to the outer list. +export function isTimelineScrollTarget( + target: EventTarget | null, + timeline: HTMLElement, + deltaY: number, +): boolean { + if (!(target instanceof Element) || !timeline.contains(target) || deltaY === 0) return false; + + for ( + let element: Element | null = target; + element && element !== timeline; + element = element.parentElement + ) { + const style = getComputedStyle(element); + if (style.overflowY !== "auto" && style.overflowY !== "scroll") continue; + + const canScroll = + deltaY < 0 + ? element.scrollTop > 0 + : element.scrollTop < element.scrollHeight - element.clientHeight; + if ( + canScroll || + style.overscrollBehaviorY === "contain" || + style.overscrollBehaviorY === "none" + ) { + return false; + } + } + return true; +} diff --git a/apps/web/src/index.css b/apps/web/src/index.css index d7c49958ff..7c242b049f 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -13,7 +13,7 @@ html[data-mobile-composer-route-transition="true"]::view-transition-new(root) { } html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-composer) { - animation-duration: 180ms; + animation-duration: var(--mobile-composer-transition-duration); animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -22,12 +22,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-image-pair(t } html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-composer) { - animation: t3-mobile-composer-old 180ms linear both; + animation: t3-mobile-composer-old var(--mobile-composer-transition-duration) linear both; mix-blend-mode: normal; } html[data-mobile-composer-route-transition="true"]::view-transition-new(t3-mobile-composer) { - animation: t3-mobile-composer-new 180ms linear both; + animation: t3-mobile-composer-new var(--mobile-composer-transition-duration) linear both; mix-blend-mode: normal; } @@ -57,11 +57,12 @@ html[data-mobile-composer-route-transition="true"]::view-transition-new(t3-mobil } html[data-mobile-composer-route-transition="true"]::view-transition-group(t3-mobile-draft-headline) { - animation-duration: 130ms; + animation-duration: var(--mobile-composer-transition-duration); } html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobile-draft-headline) { - animation: t3-mobile-draft-headline-exit 130ms cubic-bezier(0.4, 0, 1, 1) both; + animation: t3-mobile-draft-headline-exit var(--mobile-composer-transition-duration) + cubic-bezier(0.4, 0, 1, 1) both; mix-blend-mode: normal; } From edebab3f89e056fa011f5e77a2234f5147321214 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:48:35 -0600 Subject: [PATCH 092/104] fix(preview): prefer visible browser for new leases (#767) --- .../src/mcp/PreviewAutomationBroker.test.ts | 186 ++++++++++++++++++ .../server/src/mcp/PreviewAutomationBroker.ts | 36 +++- .../preview/PreviewAutomationHosts.test.tsx | 181 ++++++++++++++++- .../preview/PreviewAutomationHosts.tsx | 56 +++++- packages/contracts/src/previewAutomation.ts | 9 + 5 files changed, 448 insertions(+), 20 deletions(-) diff --git a/apps/server/src/mcp/PreviewAutomationBroker.test.ts b/apps/server/src/mcp/PreviewAutomationBroker.test.ts index ef552f9b04..bdad30d3c3 100644 --- a/apps/server/src/mcp/PreviewAutomationBroker.test.ts +++ b/apps/server/src/mcp/PreviewAutomationBroker.test.ts @@ -685,6 +685,7 @@ it.effect("pins a provider session to its initial host despite later focus chang environmentId: scope.environmentId, connectionId: "connection-stale", focused: true, + liveTabs: [{ threadId: scope.threadId, tabId: PreviewTabId.make("stale-tab") }], }); expect(yield* broker.invoke({ scope, operation: "status", input: {} })).toBe( "second", @@ -725,6 +726,190 @@ it.effect("pins a provider session to its initial host despite later focus chang ), ); +it.effect("prefers the live tab owner for new sessions without moving existing leases", () => + Effect.scoped( + Effect.gen(function* () { + const broker = yield* makeBroker; + const connections = new Map(); + for (const clientId of ["owner", "other"]) { + const requests = requestsFrom( + yield* broker.connect(makeHost({ clientId })), + (connectionId) => connections.set(clientId, connectionId), + ); + yield* Stream.runForEach(requests, (request) => + broker.respond({ + clientId, + connectionId: request.connectionId, + requestId: request.requestId, + ok: true, + result: clientId, + }), + ).pipe(Effect.forkScoped); + } + yield* Effect.yieldNow; + yield* broker.focusHost({ + clientId: "owner", + environmentId: scope.environmentId, + connectionId: connections.get("owner")!, + focused: false, + liveTabs: [ + { threadId: scope.threadId, tabId: PreviewTabId.make("signed-in"), visible: true }, + ], + }); + yield* broker.focusHost({ + clientId: "other", + environmentId: scope.environmentId, + connectionId: connections.get("other")!, + focused: true, + liveTabs: [ + { threadId: scope.threadId, tabId: PreviewTabId.make("signed-in"), visible: false }, + { threadId: scope.threadId, tabId: PreviewTabId.make("other-signed-in"), visible: false }, + { + threadId: ThreadId.make("another-thread"), + tabId: PreviewTabId.make("different-tab"), + visible: true, + }, + ], + }); + expect(yield* broker.invoke({ scope, operation: "evaluate", input: {} })).toBe( + "owner", + ); + const wrongHostError = yield* broker + .invoke({ + scope, + tabId: PreviewTabId.make("other-signed-in"), + operation: "evaluate", + input: {}, + }) + .pipe(Effect.flip); + expect(wrongHostError).toBeInstanceOf(PreviewAutomationNoAvailableHostError); + expect(yield* broker.invoke({ scope, operation: "evaluate", input: {} })).toBe( + "owner", + ); + expect( + yield* broker.invoke({ + scope: { ...scope, providerSessionId: "explicit-owner" }, + tabId: PreviewTabId.make("signed-in"), + operation: "snapshot", + input: {}, + }), + ).toBe("owner"); + expect( + yield* broker.invoke({ + scope: { ...scope, providerSessionId: "other-tab" }, + tabId: PreviewTabId.make("different-tab"), + operation: "evaluate", + input: {}, + }), + ).toBe("other"); + + yield* broker.focusHost({ + clientId: "owner", + environmentId: scope.environmentId, + connectionId: connections.get("owner")!, + focused: false, + liveTabs: [], + }); + expect(yield* broker.invoke({ scope, operation: "evaluate", input: {} })).toBe( + "owner", + ); + expect( + yield* broker.invoke({ + scope: { ...scope, providerSessionId: "after-tab-closed" }, + operation: "evaluate", + input: {}, + }), + ).toBe("other"); + }), + ), +); + +it.effect("retains capability-rich host preference when no host owns the target tab", () => + Effect.scoped( + Effect.gen(function* () { + const broker = yield* makeBroker; + let focusedConnectionId = ""; + for (const [clientId, supportedOperations] of [ + ["focused", ["status"]], + ["background", ["status", "resize"]], + ] as const) { + const requests = requestsFrom( + yield* broker.connect(makeHost({ clientId, supportedOperations })), + (connectionId) => { + if (clientId === "focused") focusedConnectionId = connectionId; + }, + ); + yield* Stream.runForEach(requests, (request) => + broker.respond({ + clientId, + connectionId: request.connectionId, + requestId: request.requestId, + ok: true, + result: clientId, + }), + ).pipe(Effect.forkScoped); + } + yield* Effect.yieldNow; + yield* broker.focusHost({ + clientId: "focused", + environmentId: scope.environmentId, + connectionId: focusedConnectionId, + focused: true, + }); + expect(yield* broker.invoke({ scope, operation: "status", input: {} })).toBe( + "background", + ); + }), + ), +); + +it.effect("fails closed when a named tab's owner cannot perform the requested operation", () => + Effect.scoped( + Effect.gen(function* () { + const broker = yield* makeBroker; + let ownerConnectionId = ""; + for (const [clientId, supportedOperations] of [ + ["owner", ["status"]], + ["other", ["status", "snapshot"]], + ] as const) { + const requests = requestsFrom( + yield* broker.connect(makeHost({ clientId, supportedOperations })), + (connectionId) => { + if (clientId === "owner") ownerConnectionId = connectionId; + }, + ); + yield* Stream.runForEach(requests, (request) => + broker.respond({ + clientId, + connectionId: request.connectionId, + requestId: request.requestId, + ok: true, + result: clientId, + }), + ).pipe(Effect.forkScoped); + } + yield* Effect.yieldNow; + const tabId = PreviewTabId.make("named-tab"); + yield* broker.focusHost({ + clientId: "owner", + environmentId: scope.environmentId, + connectionId: ownerConnectionId, + focused: false, + liveTabs: [{ threadId: scope.threadId, tabId, visible: true }], + }); + const error = yield* broker + .invoke({ + scope: { ...scope, providerSessionId: "fresh-explicit-tab" }, + tabId, + operation: "snapshot", + input: {}, + }) + .pipe(Effect.flip); + expect(error).toBeInstanceOf(PreviewAutomationNoAvailableHostError); + }), + ), +); + it.effect("does not route new operations to legacy hosts that did not advertise support", () => Effect.scoped( Effect.gen(function* () { @@ -921,6 +1106,7 @@ it.effect("fails over a pinned provider session only after its host disconnects" environmentId: scope.environmentId, connectionId: firstConnectionId, focused: true, + liveTabs: [{ threadId: scope.threadId, tabId: firstTabId }], }); expect(yield* broker.invoke({ scope, operation: "open", input: {} })).toEqual({ host: "first", diff --git a/apps/server/src/mcp/PreviewAutomationBroker.ts b/apps/server/src/mcp/PreviewAutomationBroker.ts index 527f71f591..73680eb79e 100644 --- a/apps/server/src/mcp/PreviewAutomationBroker.ts +++ b/apps/server/src/mcp/PreviewAutomationBroker.ts @@ -73,6 +73,7 @@ interface ClientConnection { readonly environmentId: PreviewAutomationHost["environmentId"]; readonly supportedOperations: ReadonlySet; readonly focused: boolean; + readonly liveTabs: NonNullable; readonly focusOrder: number; readonly queue: Queue.Queue; } @@ -376,6 +377,7 @@ export const make = Effect.gen(function* PreviewAutomationBrokerMake() { environmentId: host.environmentId, supportedOperations: new Set(host.supportedOperations ?? PREVIEW_AUTOMATION_V1_OPERATIONS), focused: false, + liveTabs: [], focusOrder: 0, queue, }; @@ -432,6 +434,7 @@ export const make = Effect.gen(function* PreviewAutomationBrokerMake() { clients.set(host.clientId, { ...currentHost, focused: host.focused, + liveTabs: host.liveTabs ?? currentHost.liveTabs, focusOrder: host.focused ? focusSequence : currentHost.focusOrder, }); return { ...current, clients, focusSequence }; @@ -492,19 +495,38 @@ export const make = Effect.gen(function* PreviewAutomationBrokerMake() { // operation is not silently moved to a newer client: the caller gets a // capability failure and can deliberately start a fresh provider // session. A dead lease is pruned above and may fail over. + const ownsTargetTab = (host: ClientConnection, visibleOnly = false) => + host.liveTabs.some( + (tab) => + tab.threadId === input.scope.threadId && + (!visibleOnly || tab.visible === true) && + (input.tabId === undefined || tab.tabId === input.tabId), + ); + const environmentHosts = Array.from(current.clients.values()).filter( + (host) => host.environmentId === input.scope.environmentId, + ); + const explicitTabOwners = + input.tabId === undefined ? [] : environmentHosts.filter((host) => ownsTargetTab(host)); + const assignedOwnsNamedTab = + explicitTabOwners.length === 0 || + explicitTabOwners.some((host) => host.clientId === assignedConnection?.clientId); + // A named tab with a reporting owner must stay on an owning host. If + // none of those owners supports the operation, fail instead of sending + // the request to a different browser with unrelated cookies and DOM. + const candidateHosts = explicitTabOwners.length > 0 ? explicitTabOwners : environmentHosts; const connection = - hasLiveAssignment && supportsOperation(assignedConnection, input.operation) + hasLiveAssignment && + assignedOwnsNamedTab && + supportsOperation(assignedConnection, input.operation) ? assignedConnection : hasLiveAssignment ? undefined - : Array.from(current.clients.values()) - .filter( - (host) => - host.environmentId === input.scope.environmentId && - supportsOperation(host, input.operation), - ) + : candidateHosts + .filter((host) => supportsOperation(host, input.operation)) .sort( (left, right) => + Number(ownsTargetTab(right, true)) - Number(ownsTargetTab(left, true)) || + Number(ownsTargetTab(right)) - Number(ownsTargetTab(left)) || right.supportedOperations.size - left.supportedOperations.size || Number(right.focused) - Number(left.focused) || right.focusOrder - left.focusOrder, diff --git a/apps/web/src/components/preview/PreviewAutomationHosts.test.tsx b/apps/web/src/components/preview/PreviewAutomationHosts.test.tsx index 75ed20cc4f..542b84907b 100644 --- a/apps/web/src/components/preview/PreviewAutomationHosts.test.tsx +++ b/apps/web/src/components/preview/PreviewAutomationHosts.test.tsx @@ -8,13 +8,22 @@ import { type PreviewOpenInput, type PreviewSessionSnapshot, } from "@t3tools/contracts"; +import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime"; +import * as Cause from "effect/Cause"; import { AsyncResult, Atom } from "effect/unstable/reactivity"; import { act } from "react"; import { create, type ReactTestRenderer } from "react-test-renderer"; import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; +import { useBrowserSurfaceStore } from "~/browser/browserSurfaceStore"; +import { previewRuntimeTabId } from "~/browser/previewRuntimeTabId"; import { __resetClientSettingsPersistenceForTests } from "~/hooks/useSettings"; -import { readThreadPreviewState, resetPreviewStateForTests } from "~/previewStateStore"; +import { + applyPreviewServerSnapshot, + readThreadPreviewState, + resetPreviewStateForTests, + applyPreviewDesktopState, +} from "~/previewStateStore"; import { appAtomRegistry, AppAtomRegistryProvider } from "~/rpc/atomRegistry"; import { PreviewAutomationHosts } from "./PreviewAutomationHosts"; @@ -31,7 +40,7 @@ const mocks = vi.hoisted(() => ({ vi.fn< (target: { environmentId: EnvironmentId; input: PreviewAutomationResponse }) => Promise >(), - focus: vi.fn(async () => undefined), + focus: vi.fn<() => Promise>>(), })); vi.mock("~/localApi", () => ({ @@ -110,12 +119,21 @@ beforeEach(async () => { vi.clearAllMocks(); mocks.getClientSettings.mockReset().mockResolvedValue(savedSettings); mocks.respond.mockReset(); + mocks.focus.mockReset().mockResolvedValue(AsyncResult.success(undefined)); __resetClientSettingsPersistenceForTests(); resetPreviewStateForTests(); + useBrowserSurfaceStore.setState({ byTabId: {} }); appAtomRegistry.set(requestsAtom, AsyncResult.initial(false)); vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); vi.stubGlobal("window", { addEventListener: vi.fn(), removeEventListener: vi.fn() }); - vi.stubGlobal("document", { hasFocus: () => false, querySelectorAll: () => [] }); + vi.stubGlobal( + "document", + Object.assign(new EventTarget(), { + hasFocus: () => false, + visibilityState: "visible", + querySelectorAll: () => [], + }), + ); await act(() => { renderer = create( @@ -161,7 +179,11 @@ describe("PreviewAutomationHosts open", () => { input: { threadId, viewport, profileId: "work" }, }); expect(mocks.getClientSettings).toHaveBeenCalledOnce(); - await expect(response.promise).resolves.toMatchObject({ requestId: "open-request", ok: true }); + await expect(response.promise).resolves.toMatchObject({ + requestId: "open-request", + ok: true, + result: { available: false, tabId: snapshot.tabId }, + }); expect(readThreadPreviewState(threadRef).snapshot).toEqual(snapshot); expect(mocks.setClientSettings).not.toHaveBeenCalled(); }); @@ -188,3 +210,154 @@ describe("PreviewAutomationHosts open", () => { expect(mocks.setClientSettings).not.toHaveBeenCalled(); }); }); + +describe("PreviewAutomationHosts ownership", () => { + it("reports only local live tabs and removes ownership when their web contents close", async () => { + await act(() => { + appAtomRegistry.set( + requestsAtom, + AsyncResult.success({ type: "connected", connectionId: "automation-connection" }), + ); + applyPreviewServerSnapshot(threadRef, snapshot); + }); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ input: expect.objectContaining({ liveTabs: [] }) }), + ); + const overlay = { + hasWebContents: true, + canGoBack: false, + canGoForward: false, + loading: false, + zoomFactor: 1, + pictureInPicture: false, + colorScheme: "system" as const, + audioMuted: false, + audible: false, + controller: "none" as const, + favicon: null, + }; + await act(() => applyPreviewDesktopState(threadRef, snapshot.tabId, overlay)); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + liveTabs: [{ threadId, tabId: snapshot.tabId, visible: false }], + }), + }), + ); + const reportCount = mocks.focus.mock.calls.length; + await act(() => + applyPreviewDesktopState(threadRef, snapshot.tabId, { ...overlay, loading: true }), + ); + expect(mocks.focus).toHaveBeenCalledTimes(reportCount); + const runtimeTabId = previewRuntimeTabId(threadRef, null, snapshot.tabId); + const owner = Symbol(); + await act(() => { + useBrowserSurfaceStore.getState().claim(runtimeTabId, owner, false); + useBrowserSurfaceStore + .getState() + .present(runtimeTabId, owner, { x: 0, y: 0, width: 800, height: 600 }, true, 0, 1); + }); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + liveTabs: [{ threadId, tabId: snapshot.tabId, visible: true }], + }), + }), + ); + for (const visibilityState of ["hidden", "visible"]) { + await act(() => { + Object.assign(document, { visibilityState }); + document.dispatchEvent(new Event("visibilitychange")); + }); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + focused: false, + liveTabs: [{ threadId, tabId: snapshot.tabId, visible: visibilityState === "visible" }], + }), + }), + ); + } + await act(() => { + appAtomRegistry.set( + requestsAtom, + AsyncResult.success({ type: "connected", connectionId: "reconnected" }), + ); + }); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ + connectionId: "reconnected", + liveTabs: [{ threadId, tabId: snapshot.tabId, visible: true }], + }), + }), + ); + await act(() => + applyPreviewDesktopState(threadRef, snapshot.tabId, { ...overlay, hasWebContents: false }), + ); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ input: expect.objectContaining({ liveTabs: [] }) }), + ); + }); + + it.each([false, true])( + "retries failed reports without clearing newer connection reports (reconnect: %s)", + async (reconnect) => { + const report = deferred>>(); + mocks.focus.mockReturnValueOnce(report.promise); + await act(() => { + appAtomRegistry.set( + requestsAtom, + AsyncResult.success({ type: "connected", connectionId: "first" }), + ); + }); + if (reconnect) { + await act(() => { + appAtomRegistry.set( + requestsAtom, + AsyncResult.success({ type: "connected", connectionId: "second" }), + ); + }); + } + await act(async () => { + report.resolve(AsyncResult.failure(Cause.fail(new Error("Focus report failed")))); + await report.promise; + }); + expect(mocks.focus).toHaveBeenCalledTimes(reconnect ? 2 : 1); + await act(() => { + document.dispatchEvent(new Event("visibilitychange")); + }); + expect(mocks.focus).toHaveBeenCalledTimes(2); + expect(mocks.focus).toHaveBeenLastCalledWith( + expect.objectContaining({ + input: expect.objectContaining({ connectionId: reconnect ? "second" : "first" }), + }), + ); + }, + ); + + it("does not claim an available runtime from a server snapshot alone", async () => { + const response = deferred(); + mocks.respond.mockImplementationOnce(async ({ input }) => response.resolve(input)); + await act(async () => { + applyPreviewServerSnapshot(threadRef, snapshot); + appAtomRegistry.set( + requestsAtom, + AsyncResult.success({ + ...requestEvent, + request: { + ...requestEvent.request, + operation: "status", + tabId: snapshot.tabId, + input: {}, + }, + }), + ); + await response.promise; + }); + await expect(response.promise).resolves.toMatchObject({ + ok: true, + result: { available: false, tabId: snapshot.tabId }, + }); + }); +}); diff --git a/apps/web/src/components/preview/PreviewAutomationHosts.tsx b/apps/web/src/components/preview/PreviewAutomationHosts.tsx index 78f1ea8f87..b7d8b5c9db 100644 --- a/apps/web/src/components/preview/PreviewAutomationHosts.tsx +++ b/apps/web/src/components/preview/PreviewAutomationHosts.tsx @@ -1,6 +1,7 @@ "use client"; import { RegistryContext, useAtomSet, useAtomValue } from "@effect/atom-react"; +import { parseScopedThreadKey } from "@t3tools/client-runtime/environment"; import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime"; import { FILL_PREVIEW_VIEWPORT, @@ -22,12 +23,14 @@ import { import { resolvePreviewViewport } from "@t3tools/shared/previewViewport"; import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import { Atom } from "effect/unstable/reactivity"; +import { useShallow } from "zustand/react/shallow"; import { applyPreviewServerSnapshot, readThreadPreviewState, reconcilePreviewServerSessions, updatePreviewServerSnapshot, + useActivePreviewSessions, } from "~/previewStateStore"; import { browserMiniPlayerSource, @@ -254,7 +257,7 @@ const currentStatus = async ( } const navStatus = snapshot?.navStatus; return { - available: Boolean(previewBridge?.automation), + available: false, visible, tabId, url: navStatus && navStatus._tag !== "Idle" ? navStatus.url : null, @@ -296,6 +299,30 @@ export function PreviewAutomationHosts() { function PreviewAutomationHost(props: { readonly environmentId: EnvironmentId }) { const { environmentId } = props; + const previewSessions = useActivePreviewSessions(); + const visibleRuntimeTabIds = useBrowserSurfaceStore( + useShallow((state) => + Object.keys(state.byTabId).filter((tabId) => state.byTabId[tabId]?.visible), + ), + ); + const liveTabs = useMemo( + () => + Object.entries(previewSessions).flatMap(([key, state]) => { + const ref = parseScopedThreadKey(key); + if (ref?.environmentId !== environmentId) return []; + return Object.values(state.sessions) + .filter((tab) => state.desktopByTabId[tab.tabId]?.hasWebContents) + .map((tab) => ({ + threadId: ref.threadId, + tabId: tab.tabId, + visible: visibleRuntimeTabIds.includes( + previewRuntimeTabId(ref, state.serverEpoch, tab.tabId), + ), + })); + }), + [environmentId, previewSessions, visibleRuntimeTabIds], + ); + const lastFocusReportRef = useRef(null); const registry = useContext(RegistryContext); const [automationClientId] = useState(createPreviewAutomationClientId); const initialAutomationHost = useMemo( @@ -816,24 +843,35 @@ function PreviewAutomationHost(props: { readonly environmentId: EnvironmentId }) useEffect(() => { const report = () => { if (!automationConnectionId) return; - void focusAutomationHost({ + const input = { + clientId: automationClientId, environmentId, - input: { - clientId: automationClientId, - environmentId, - connectionId: automationConnectionId, - focused: document.hasFocus(), - }, + connectionId: automationConnectionId, + focused: document.hasFocus() && document.visibilityState === "visible", + liveTabs: liveTabs.map((tab) => ({ + ...tab, + visible: tab.visible && document.visibilityState === "visible", + })), + }; + const reportKey = JSON.stringify(input); + if (lastFocusReportRef.current === reportKey) return; + lastFocusReportRef.current = reportKey; + void focusAutomationHost({ environmentId, input }).then((result) => { + if (result._tag === "Failure" && lastFocusReportRef.current === reportKey) { + lastFocusReportRef.current = null; + } }); }; report(); window.addEventListener("focus", report); window.addEventListener("blur", report); + document.addEventListener("visibilitychange", report); return () => { window.removeEventListener("focus", report); window.removeEventListener("blur", report); + document.removeEventListener("visibilitychange", report); }; - }, [automationClientId, automationConnectionId, environmentId, focusAutomationHost]); + }, [automationClientId, automationConnectionId, environmentId, focusAutomationHost, liveTabs]); return null; } diff --git a/packages/contracts/src/previewAutomation.ts b/packages/contracts/src/previewAutomation.ts index 1ec6d74f55..445dd6fc46 100644 --- a/packages/contracts/src/previewAutomation.ts +++ b/packages/contracts/src/previewAutomation.ts @@ -590,6 +590,15 @@ export const PreviewAutomationHostFocus = Schema.Struct({ ...PreviewAutomationHostIdentity.fields, connectionId: PreviewAutomationConnectionId, focused: Schema.Boolean, + liveTabs: Schema.optional( + Schema.Array( + Schema.Struct({ + threadId: ThreadId, + tabId: PreviewTabId, + visible: Schema.optional(Schema.Boolean), + }), + ), + ), }); export type PreviewAutomationHostFocus = typeof PreviewAutomationHostFocus.Type; From 10c3bac4ecc3f5a3ac4a3a41ccb9f77714ef0ff0 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:53:59 -0600 Subject: [PATCH 093/104] fix(preview): preserve picking and align animated surfaces (#839) * fix(desktop): keep preview picking active across subframe navigation (#9741) Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit 1ab2dfb5a7bd2996f79407b5d02cae6132a7626c) * fix(web): keep browser pages aligned during panel animations (#12329) (cherry picked from commit 4cc984fedc33184c0a0fb67ea17fb02902ae2224) * fix(preview): preserve picking and align animated surfaces Keep element picking armed across subframe navigation, observe inline preview panel movement, and use the shared resize lifecycle for the sidebar rail. --------- Co-authored-by: Simone Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Julius Marminge --- apps/desktop/src/preview/Manager.test.ts | 21 +- apps/desktop/src/preview/Manager.ts | 9 +- .../src/browser/BrowserSurfaceSlot.test.tsx | 55 ++++ apps/web/src/browser/BrowserSurfaceSlot.tsx | 4 + apps/web/src/components/ui/sidebar.tsx | 269 ++++++------------ 5 files changed, 165 insertions(+), 193 deletions(-) create mode 100644 apps/web/src/browser/BrowserSurfaceSlot.test.tsx diff --git a/apps/desktop/src/preview/Manager.test.ts b/apps/desktop/src/preview/Manager.test.ts index ae57daf62b..ec0ae4432f 100644 --- a/apps/desktop/src/preview/Manager.test.ts +++ b/apps/desktop/src/preview/Manager.test.ts @@ -3713,7 +3713,10 @@ describe("PreviewManager", () => { listeners.set(event, listener); }), once: vi.fn((event: string, listener: (...args: unknown[]) => void) => { - listeners.set(event, listener); + listeners.set(event, (...args) => { + listeners.delete(event); + listener(...args); + }); }), off: vi.fn(), ipc: { on: vi.fn(), off: vi.fn(), removeListener: vi.fn() }, @@ -3735,11 +3738,23 @@ describe("PreviewManager", () => { const pick = yield* manager.pickElement("tab_1").pipe(Effect.forkChild); yield* Effect.yieldNow; - listeners.get("did-start-navigation")?.({}, "about:blank", false, false); + listeners.get("did-start-navigation")?.({ + url: "about:blank", + isSameDocument: false, + isMainFrame: false, + frame: null, + }); yield* Effect.yieldNow; expect(pick.pollUnsafe()).toBeUndefined(); - listeners.get("did-start-navigation")?.({}, "https://example.com/next", false, true); + listeners.get("did-start-navigation")?.({ + url: "https://example.com/next", + isSameDocument: false, + isMainFrame: true, + frame: null, + }); + yield* Effect.yieldNow; + expect(pick.pollUnsafe()).toBeDefined(); expect(yield* Fiber.join(pick)).toBeNull(); }), ), diff --git a/apps/desktop/src/preview/Manager.ts b/apps/desktop/src/preview/Manager.ts index 3162e878ee..5670f200f2 100644 --- a/apps/desktop/src/preview/Manager.ts +++ b/apps/desktop/src/preview/Manager.ts @@ -2681,12 +2681,9 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function }; const onDestroyed = () => settle(null); const onNavigated = ( - _event: Electron.Event, - _url: string, - _isInPlace: boolean, - isMainFrame: boolean, + event: Electron.Event, ) => { - if (isMainFrame) settle(null); + if (event.isMainFrame) settle(null); }; const registerPickElement = Effect.fn("PreviewManager.registerPickElement")(function* () { // Two picks on one tab can overlap. Swap this session in and cancel @@ -2707,7 +2704,7 @@ const makeNativeOperations = Effect.fn("PreviewManager.makeOperations")(function yield* attempt({ operation: "pickElement.register", tabId, webContentsId: wc.id }, () => { wc.ipc.on(ELEMENT_PICKED_CHANNEL, onMessage); wc.once("destroyed", onDestroyed); - wc.once("did-start-navigation", onNavigated); + wc.on("did-start-navigation", onNavigated); if (!wc.isFocused()) wc.focus(); wc.send(START_PICK_CHANNEL, annotationTheme); }); diff --git a/apps/web/src/browser/BrowserSurfaceSlot.test.tsx b/apps/web/src/browser/BrowserSurfaceSlot.test.tsx new file mode 100644 index 0000000000..58ba8e3700 --- /dev/null +++ b/apps/web/src/browser/BrowserSurfaceSlot.test.tsx @@ -0,0 +1,55 @@ +import { act } from "react"; +import { create } from "react-test-renderer"; +import { afterEach, expect, it, vi } from "vite-plus/test"; + +import { BrowserSurfaceSlot } from "./BrowserSurfaceSlot"; + +const { present, release } = vi.hoisted(() => ({ present: vi.fn(() => true), release: vi.fn() })); +vi.mock("./browserSurfaceStore", () => ({ + acquireBrowserSurface: () => ({ present, release }), +})); + +afterEach(() => { + vi.unstubAllGlobals(); + vi.clearAllMocks(); +}); + +it("tracks an inline panel moving its browser slot and cleans up the surface lease", async () => { + let panelResize: (() => void) | undefined; + const panel = {}; + const slot = { + closest: (selector: string) => + selector === '[data-preview-panel-mode="inline"]' ? panel : null, + getBoundingClientRect: () => ({ x: position, y: 8, width: 320, height: 240 }), + }; + let position = 20; + const observed: unknown[] = []; + const disconnect = vi.fn(); + vi.stubGlobal( + "ResizeObserver", + class { + constructor(private readonly callback: () => void) {} + observe(target: unknown) { + observed.push(target); + if (target === panel) panelResize = this.callback; + } + disconnect = disconnect; + }, + ); + vi.stubGlobal("window", { addEventListener: vi.fn(), removeEventListener: vi.fn() }); + let renderer: ReturnType; + await act(() => { + renderer = create(, { + createNodeMock: () => slot, + }); + }); + expect(observed).toEqual([slot, panel]); + expect(present).toHaveBeenCalledWith({ x: 20, y: 8, width: 320, height: 240 }, true, 0, 30); + position = 40; + // The panel observer must remeasure even though the slot's own dimensions did not change. + await act(() => panelResize?.()); + expect(present).toHaveBeenLastCalledWith({ x: 40, y: 8, width: 320, height: 240 }, true, 0, 30); + await act(() => renderer!.unmount()); + expect(disconnect).toHaveBeenCalledOnce(); + expect(release).toHaveBeenCalledOnce(); +}); diff --git a/apps/web/src/browser/BrowserSurfaceSlot.tsx b/apps/web/src/browser/BrowserSurfaceSlot.tsx index 3de3ed586c..b875e4c221 100644 --- a/apps/web/src/browser/BrowserSurfaceSlot.tsx +++ b/apps/web/src/browser/BrowserSurfaceSlot.tsx @@ -64,6 +64,10 @@ export function BrowserSurfaceSlot(props: { update(); const observer = new ResizeObserver(update); observer.observe(element); + // Inline panels animate their outer width while keeping the content at + // full width. The slot moves without resizing, so measure on shell resizes too. + const panel = element.closest('[data-preview-panel-mode="inline"]'); + if (panel) observer.observe(panel); window.addEventListener("resize", update); window.addEventListener("scroll", update, true); return () => { diff --git a/apps/web/src/components/ui/sidebar.tsx b/apps/web/src/components/ui/sidebar.tsx index a3eef0cbf0..2eee372713 100644 --- a/apps/web/src/components/ui/sidebar.tsx +++ b/apps/web/src/components/ui/sidebar.tsx @@ -19,6 +19,7 @@ import { Skeleton } from "~/components/ui/skeleton"; import { Tooltip, TooltipPopup, TooltipTrigger } from "~/components/ui/tooltip"; import { useIsMobile } from "~/hooks/useMediaQuery"; import { getLocalStorageItem, setLocalStorageItem } from "~/hooks/useLocalStorage"; +import { useResizeDrag } from "~/hooks/useResizeDrag"; import { resolveSidebarState, type ResponsiveSidebarState } from "./sidebarState"; import * as Schema from "effect/Schema"; @@ -356,6 +357,7 @@ function SidebarRail({ className, onClick, onPointerCancel, + onLostPointerCapture, onPointerDown, onPointerMove, onPointerUp, @@ -365,182 +367,79 @@ function SidebarRail({ const sidebarInstance = React.use(SidebarInstanceContext); const railRef = React.useRef(null); const suppressClickRef = React.useRef(false); - const resizeStateRef = React.useRef<{ - moved: boolean; - pointerId: number; - pendingWidth: number; - rail: HTMLButtonElement; - rafId: number | null; - sidebarRoot: HTMLElement; - side: "left" | "right"; - startWidth: number; - startX: number; - transitionTargets: HTMLElement[]; - width: number; - wrapper: HTMLElement; - } | null>(null); const resolvedResizable = sidebarInstance?.resizable ?? null; + const latestResizable = React.useRef(resolvedResizable); + React.useLayoutEffect(() => { + latestResizable.current = resolvedResizable; + }, [resolvedResizable]); const canResize = resolvedResizable !== null && open; const railLabel = canResize ? "Resize Sidebar" : "Toggle Sidebar"; const railTitle = canResize ? "Drag to resize sidebar" : "Toggle Sidebar"; + const resize = useResizeDrag((event) => { + if (!resolvedResizable || !open) return null; + const rail = event.currentTarget; + const wrapper = rail.closest("[data-slot='sidebar-wrapper']"); + const sidebarRoot = rail.closest("[data-slot='sidebar']"); + const sidebarContainer = sidebarRoot?.querySelector( + "[data-slot='sidebar-container']", + ); + if (!wrapper || !sidebarRoot || !sidebarContainer) return null; - const stopResize = React.useCallback( - (pointerId: number) => { - const resizeState = resizeStateRef.current; - if (!resizeState) { - return; - } - if (resizeState.rafId !== null) { - window.cancelAnimationFrame(resizeState.rafId); - } - resizeState.transitionTargets.forEach((element) => { - element.style.removeProperty("transition-duration"); - }); - if (resolvedResizable?.storageKey && typeof window !== "undefined") { - setLocalStorageItem(resolvedResizable.storageKey, resizeState.width, Schema.Finite); - } - resolvedResizable?.onResize?.(resizeState.width); - resizeStateRef.current = null; - if (resizeState.rail.hasPointerCapture(pointerId)) { - resizeState.rail.releasePointerCapture(pointerId); - } - document.body.style.removeProperty("cursor"); - document.body.style.removeProperty("user-select"); - }, - [resolvedResizable], - ); - - const handlePointerDown = React.useCallback( - (event: React.PointerEvent) => { - onPointerDown?.(event); - if (event.defaultPrevented) return; - if (!resolvedResizable || !open || event.button !== 0) return; - - const wrapper = event.currentTarget.closest("[data-slot='sidebar-wrapper']"); - const sidebarRoot = event.currentTarget.closest("[data-slot='sidebar']"); - if (!wrapper || !sidebarRoot) { - return; - } - - const sidebarContainer = sidebarRoot.querySelector( - "[data-slot='sidebar-container']", - ); - if (!sidebarContainer) { - return; - } - - const startWidth = sidebarContainer.getBoundingClientRect().width; - const initialWidth = clampSidebarWidth(startWidth, resolvedResizable); - const transitionTargets = [ - sidebarRoot.querySelector("[data-slot='sidebar-gap']"), - sidebarRoot.querySelector("[data-slot='sidebar-container']"), - ].filter((element): element is HTMLElement => element !== null); - transitionTargets.forEach((element) => { - element.style.setProperty("transition-duration", "0ms"); - }); - - event.preventDefault(); - event.stopPropagation(); - resizeStateRef.current = { - moved: false, - pointerId: event.pointerId, - pendingWidth: initialWidth, - rail: event.currentTarget, - rafId: null, - sidebarRoot, - side: sidebarInstance?.side ?? "left", - startWidth: initialWidth, - startX: event.clientX, - transitionTargets, - width: initialWidth, - wrapper, - }; - wrapper.style.setProperty("--sidebar-width", `${initialWidth}px`); - event.currentTarget.setPointerCapture(event.pointerId); - document.body.style.cursor = "col-resize"; - document.body.style.userSelect = "none"; - }, - [onPointerDown, open, resolvedResizable, sidebarInstance?.side], - ); - - const handlePointerMove = React.useCallback( - (event: React.PointerEvent) => { - onPointerMove?.(event); - if (event.defaultPrevented) return; - const resizeState = resizeStateRef.current; - if (!resizeState || resizeState.pointerId !== event.pointerId || !resolvedResizable) return; - - event.preventDefault(); - const delta = - resizeState.side === "right" - ? resizeState.startX - event.clientX - : event.clientX - resizeState.startX; - if (Math.abs(delta) > 2) { - resizeState.moved = true; - } - resizeState.pendingWidth = clampSidebarWidth( - resizeState.startWidth + delta, - resolvedResizable, - ); - if (resizeState.rafId !== null) { - return; - } - - resizeState.rafId = window.requestAnimationFrame(() => { - const activeResizeState = resizeStateRef.current; - if (!activeResizeState || !resolvedResizable) return; + const side = sidebarInstance?.side ?? "left"; + let width = clampSidebarWidth( + sidebarContainer.getBoundingClientRect().width, + resolvedResizable, + ); + const transitionTargets = [ + sidebarRoot.querySelector("[data-slot='sidebar-gap']"), + sidebarContainer, + ].filter((element): element is HTMLElement => element !== null); + transitionTargets.forEach((element) => { + element.style.setProperty("transition-duration", "0ms"); + }); + wrapper.style.setProperty("--sidebar-width", `${width}px`); - activeResizeState.rafId = null; - const nextWidth = activeResizeState.pendingWidth; + return { + width, + edge: side === "left" ? "right" : "left", + resize(value) { + const options = latestResizable.current; + if (!options) return width; + const nextWidth = clampSidebarWidth(value, options); const accepted = - resolvedResizable.shouldAcceptWidth?.({ - currentWidth: activeResizeState.width, + options.shouldAcceptWidth?.({ + currentWidth: width, nextWidth, - rail: activeResizeState.rail, - side: activeResizeState.side, - sidebarRoot: activeResizeState.sidebarRoot, - wrapper: activeResizeState.wrapper, + rail, + side, + sidebarRoot, + wrapper, }) ?? true; - if (!accepted) { - return; + if (accepted) { + wrapper.style.setProperty("--sidebar-width", `${nextWidth}px`); + width = nextWidth; } - - activeResizeState.wrapper.style.setProperty("--sidebar-width", `${nextWidth}px`); - activeResizeState.width = nextWidth; - }); - }, - [onPointerMove, resolvedResizable], - ); - - const endResizeInteraction = React.useCallback( - (event: React.PointerEvent) => { - const resizeState = resizeStateRef.current; - if (!resizeState || resizeState.pointerId !== event.pointerId) return; - - event.preventDefault(); - suppressClickRef.current = resizeState.moved; - stopResize(event.pointerId); - }, - [stopResize], - ); - - const handlePointerUp = React.useCallback( - (event: React.PointerEvent) => { - onPointerUp?.(event); - if (event.defaultPrevented) return; - endResizeInteraction(event); - }, - [endResizeInteraction, onPointerUp], - ); - - const handlePointerCancel = React.useCallback( - (event: React.PointerEvent) => { - onPointerCancel?.(event); - if (event.defaultPrevented) return; - endResizeInteraction(event); - }, - [endResizeInteraction, onPointerCancel], - ); + return width; + }, + finish(finalWidth, moved) { + suppressClickRef.current = moved; + const options = latestResizable.current; + if (options?.storageKey) { + try { + setLocalStorageItem(options.storageKey, finalWidth, Schema.Finite); + } catch (error) { + console.error("Could not persist sidebar width.", error); + } + } + options?.onResize?.(finalWidth); + }, + cleanup() { + transitionTargets.forEach((element) => { + element.style.removeProperty("transition-duration"); + }); + }, + }; + }); const handleClick = React.useCallback( (event: React.MouseEvent) => { @@ -582,20 +481,6 @@ function SidebarRail({ resolvedResizable.onResize?.(clampedWidth); }, [resolvedResizable]); - React.useEffect(() => { - return () => { - const resizeState = resizeStateRef.current; - if (resizeState?.rafId != null) { - window.cancelAnimationFrame(resizeState.rafId); - } - resizeState?.transitionTargets.forEach((element) => { - element.style.removeProperty("transition-duration"); - }); - document.body.style.removeProperty("cursor"); - document.body.style.removeProperty("user-select"); - }; - }, []); - return ( { + onLostPointerCapture?.(event); + if (!event.defaultPrevented) resize.onLostPointerCapture(event); + }} + onPointerCancel={(event) => { + onPointerCancel?.(event); + if (!event.defaultPrevented) resize.onPointerCancel(event); + }} + onPointerDown={(event) => { + onPointerDown?.(event); + if (!event.defaultPrevented) resize.onPointerDown(event); + }} + onPointerMove={(event) => { + onPointerMove?.(event); + if (!event.defaultPrevented) resize.onPointerMove(event); + }} + onPointerUp={(event) => { + onPointerUp?.(event); + if (!event.defaultPrevented) resize.onPointerUp(event); + }} ref={railRef} tabIndex={-1} type="button" From 9d95224274eddcc8eaa3bded0eb93aef989a48f9 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:54:33 -0600 Subject: [PATCH 094/104] feat(mobile): rename threads from list menus (#786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mobile): rename threads from list menus * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record p… * feat(web): filter sidebar from thread menu (#821) * feat(web): add bounded route history shortcuts on desktop (#803) * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(… * fix(web): add local dropped folders as path chips (#818) * feat(mobile): search projects when starting a task (#796) * feat(mobile): favorite models in thread settings (#790) * test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture * fix(desktop): keep SnapShot shortcut helper out of Dock (#778) * fix(mobile): wait for deep-link thread hydration (#764) * fix(relay): stale Relay workers no longer flood a reopened thread (#852) * fix(relay): stop replaying stale Relay workers into reopened threads The startup receipt scan adopts every Relay MCP tool call ever recorded, so the first boot after the worker bridge shipped bound 284 long-finished jobs across 207 old turns. None could be observed, and each one published an idle "Relay observer unavailable" row into its original turn, dated now -- a wall of stale subagent cards on reopening the thread. An outage row now only explains a worker the thread already started, and an announced outage pushes its recheck back out instead of spawning the Relay CLI on every sweep for the life of the process. * fix(relay): clear the stale Relay outage rows already written The bridge no longer announces a worker it never started, but every projection that booted the earlier build still holds the idle rows it wrote. Drop the outage rows whose task has no start in the same thread, leaving the ones that explain a worker the thread really ran. * fix(relay): retire adopted jobs Relay reports as gone An outage that had already been announced returned before pushing its recheck back out, so every binding kept a Relay CLI spawn on each sweep. Adopted history made that 284 spawns a sweep on one projection. Push the recheck out for a worker that may still return, and retire a binding outright once Relay itself reports the job gone and nothing about it ever reached the thread. Absent Relay says nothing about the job, so that case keeps the binding and only slows the recheck. The four copies of the teardown are now one helper. * fix(relay): type the test engine helper against the SqlClient service * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(relay): retire a lost job for good instead of re-probing every boot Retirement only lived in memory, so every job the environment had ever run was adopted and re-probed on each start -- a cost that grows with history and never settles. Record the retirement on the binding row itself. The activity upserts on its id, so this writes no new row and stays out of the transcript, and boot skips the binding unless an activation was recorded after the watermark. A resume outranks the retirement and wakes the worker, then answers to the ordinary rules again. * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762… * fix(mobile): keep ordinary offline outbox retries out of warnings (#783) * fix(mobile): keep ordinary offline outbox retries out of warnings * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposi… * fix(web): respect panel motion and nested timeline scrolling (#750) Adapt T3 Code sources da6a85b1365993d0ff2a79698cd82498de247d8a, 7c2702d68ae2e10fa9d3a08c996ed47c393dcffd, and 68607c5a99147710b92228e0642f6c0a642e32a6 for Pylon composer and context controls. --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari --- .../src/components/ConfirmDialogHost.tsx | 83 ++++++++++++++----- .../src/features/home/HomeRouteScreen.tsx | 2 + apps/mobile/src/features/home/HomeScreen.tsx | 9 ++ .../src/features/home/useThreadListActions.ts | 51 +++++++++++- .../threads/ThreadNavigationSidebar.tsx | 4 + .../features/threads/thread-list-items.tsx | 10 ++- .../features/threads/thread-list-v2-items.tsx | 33 +++++--- .../threads/thread-title-rename.test.ts | 21 +++++ .../features/threads/thread-title-rename.ts | 14 ++++ 9 files changed, 190 insertions(+), 37 deletions(-) create mode 100644 apps/mobile/src/features/threads/thread-title-rename.test.ts create mode 100644 apps/mobile/src/features/threads/thread-title-rename.ts diff --git a/apps/mobile/src/components/ConfirmDialogHost.tsx b/apps/mobile/src/components/ConfirmDialogHost.tsx index 521c5e36c3..d7db39d39a 100644 --- a/apps/mobile/src/components/ConfirmDialogHost.tsx +++ b/apps/mobile/src/components/ConfirmDialogHost.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { Modal, Pressable, View } from "react-native"; +import { Modal, Pressable, TextInput, View } from "react-native"; import { cn } from "../lib/cn"; import { AppText } from "./AppText"; @@ -14,7 +14,20 @@ export type ConfirmDialogRequest = { readonly onCancel?: () => void; }; -let presentRequest: ((request: ConfirmDialogRequest) => void) | null = null; +export type TextInputDialogRequest = { + readonly title: string; + readonly initialValue: string; + readonly cancelText?: string; + readonly confirmText: string; + readonly onConfirm: (value: string) => void; + readonly onCancel?: () => void; +}; + +type DialogRequest = + | { readonly kind: "confirm"; readonly request: ConfirmDialogRequest } + | { readonly kind: "text-input"; readonly request: TextInputDialogRequest }; + +let presentRequest: ((request: DialogRequest) => void) | null = null; /** * Imperative confirm dialog, Alert.alert-shaped. Native iOS alerts already @@ -23,7 +36,11 @@ let presentRequest: ((request: ConfirmDialogRequest) => void) | null = null; * once. Requires ConfirmDialogHost to be mounted at the app root. */ export function showConfirmDialog(request: ConfirmDialogRequest): void { - presentRequest?.(request); + presentRequest?.({ kind: "confirm", request }); +} + +export function showTextInputDialog(request: TextInputDialogRequest): void { + presentRequest?.({ kind: "text-input", request }); } /** @@ -33,42 +50,64 @@ export function showConfirmDialog(request: ConfirmDialogRequest): void { * button color and a dimmer message than the title. */ export function ConfirmDialogHost() { - const [request, setRequest] = useState(null); + const [presented, setPresented] = useState(null); + const [inputValue, setInputValue] = useState(""); useEffect(() => { - presentRequest = setRequest; + presentRequest = (request) => { + setInputValue(request.kind === "text-input" ? request.request.initialValue : ""); + setPresented(request); + }; return () => { presentRequest = null; }; }, []); const handleCancel = useCallback(() => { - request?.onCancel?.(); - setRequest(null); - }, [request]); + presented?.request.onCancel?.(); + setPresented(null); + }, [presented]); const handleConfirm = useCallback(() => { - request?.onConfirm(); - setRequest(null); - }, [request]); + if (presented?.kind === "confirm") { + presented.request.onConfirm(); + } else if (presented?.kind === "text-input") { + presented.request.onConfirm(inputValue); + } + setPresented(null); + }, [inputValue, presented]); + + const confirmDisabled = presented?.kind === "text-input" && inputValue.trim().length === 0; return ( - {request === null ? null : ( + {presented === null ? null : ( - {request.title} - {request.message === undefined ? null : ( + {presented.request.title} + {presented.kind === "confirm" && presented.request.message !== undefined ? ( - {request.message} + {presented.request.message} - )} + ) : null} + {presented.kind === "text-input" ? ( + + ) : null} - {request.cancelText ?? "Cancel"} + {presented.request.cancelText ?? "Cancel"} - {request.confirmText} + {presented.request.confirmText} diff --git a/apps/mobile/src/features/home/HomeRouteScreen.tsx b/apps/mobile/src/features/home/HomeRouteScreen.tsx index 00731ce5ae..b2da5af8ed 100644 --- a/apps/mobile/src/features/home/HomeRouteScreen.tsx +++ b/apps/mobile/src/features/home/HomeRouteScreen.tsx @@ -50,6 +50,7 @@ export function HomeRouteScreen() { pinThread, unpinThread, moveThread, + renameThread, regenerateThreadTitle, unsettleThread, } = useThreadListActions(); @@ -200,6 +201,7 @@ export function HomeRouteScreen() { onPinThread={pinThread} onUnpinThread={unpinThread} onMoveThread={moveThread} + onRenameThread={renameThread} onRegenerateThreadTitle={regenerateThreadTitle} onEnvironmentChange={setSelectedEnvironmentId} onProjectChange={setSelectedProjectKey} diff --git a/apps/mobile/src/features/home/HomeScreen.tsx b/apps/mobile/src/features/home/HomeScreen.tsx index ea7e3db477..883c3e1184 100644 --- a/apps/mobile/src/features/home/HomeScreen.tsx +++ b/apps/mobile/src/features/home/HomeScreen.tsx @@ -126,6 +126,7 @@ interface HomeScreenProps { thread: EnvironmentThreadShell, direction: ThreadMoveDestination, ) => Promise; + readonly onRenameThread: (thread: EnvironmentThreadShell) => void; readonly onRegenerateThreadTitle: (thread: EnvironmentThreadShell) => Promise; readonly onSelectPendingTask: (pendingTask: PendingNewTask) => void; readonly onDeletePendingTask: (pendingTask: PendingNewTask) => void; @@ -550,6 +551,10 @@ export function HomeScreen(props: HomeScreenProps) { }, [props.onRegenerateThreadTitle], ); + const handleRenameThread = useCallback( + (thread: EnvironmentThreadShell) => props.onRenameThread(thread), + [props.onRenameThread], + ); const handleDeleteThread = props.onDeleteThread; const handleUnsettleThread = props.onUnsettleThread; // The settled tail renders in pages; expansion resets when the filter @@ -896,6 +901,7 @@ export function HomeScreen(props: HomeScreenProps) { onSelectThread={props.onSelectThread} onDeleteThread={handleDeleteThread} onArchiveThread={props.onArchiveThread} + onRenameThread={handleRenameThread} onRegenerateThreadTitle={handleRegenerateThreadTitle} titleRegenerationSupported={titleRegenerationEnvironmentIds.has(thread.environmentId)} settlementSupported={settlementEnvironmentIds.has(thread.environmentId)} @@ -932,6 +938,7 @@ export function HomeScreen(props: HomeScreenProps) { handleMoveThread, handlePinThread, handleRegenerateThreadTitle, + handleRenameThread, handleSettleThread, handleSnoozeThread, handleUnpinThread, @@ -1066,6 +1073,7 @@ export function HomeScreen(props: HomeScreenProps) { searchQuery={props.searchQuery} onArchiveThread={props.onArchiveThread} onDeleteThread={props.onDeleteThread} + onRenameThread={handleRenameThread} onRegenerateThreadTitle={handleRegenerateThreadTitle} titleRegenerationSupported={titleRegenerationEnvironmentIds.has(thread.environmentId)} onSelectThread={props.onSelectThread} @@ -1090,6 +1098,7 @@ export function HomeScreen(props: HomeScreenProps) { handleSwipeableClose, handleSwipeableWillOpen, handleRegenerateThreadTitle, + handleRenameThread, machineByEnvironmentId, projectCwdByKey, queuedThreadKeys, diff --git a/apps/mobile/src/features/home/useThreadListActions.ts b/apps/mobile/src/features/home/useThreadListActions.ts index 72e45c9bbb..9b59911088 100644 --- a/apps/mobile/src/features/home/useThreadListActions.ts +++ b/apps/mobile/src/features/home/useThreadListActions.ts @@ -4,10 +4,10 @@ import { canSnooze, effectiveSnoozed } from "@t3tools/client-runtime/state/threa import * as Cause from "effect/Cause"; import * as Haptics from "expo-haptics"; import { useCallback, useRef } from "react"; -import { Alert } from "react-native"; +import { Alert, Platform } from "react-native"; import { withThreadDismissal } from "./thread-dismissal"; -import { showConfirmDialog } from "../../components/ConfirmDialogHost"; +import { showConfirmDialog, showTextInputDialog } from "../../components/ConfirmDialogHost"; import { scopedThreadKey } from "../../lib/scopedEntities"; import { refreshArchivedThreadsForEnvironment } from "../archive/useArchivedThreadSnapshots"; import { pinOrderKeyBetween } from "@t3tools/client-runtime/state/thread-sort"; @@ -27,6 +27,7 @@ import { threadDropLifecycle, } from "../threads/threadOrder"; import { getThreadListV2OrderedSection } from "../threads/threadListV2"; +import { resolveThreadTitleRename } from "../threads/thread-title-rename"; /** Version skew: never send settle/unsettle to a server that predates them (capability defaults false on decode for older servers). */ @@ -240,6 +241,7 @@ export function useThreadListActions(): { thread: EnvironmentThreadShell, direction: ThreadMoveDestination, ) => Promise; + readonly renameThread: (thread: EnvironmentThreadShell) => void; readonly regenerateThreadTitle: (thread: EnvironmentThreadShell) => Promise; } { const executeAction = useThreadActionExecutor(); @@ -474,6 +476,50 @@ export function useThreadListActions(): { }, [updateThreadMetadata], ); + const renameThread = useCallback( + (thread: EnvironmentThreadShell) => { + const commit = (title: string) => { + const resolution = resolveThreadTitleRename({ title, originalTitle: thread.title }); + if (resolution.action === "reject-empty") { + Alert.alert("Could not rename thread", "Thread title cannot be empty."); + return; + } + if (resolution.action === "noop") return; + selectionHaptic(); + void updateThreadMetadata({ + environmentId: thread.environmentId, + input: { threadId: thread.id, title: resolution.title }, + }).then((result) => { + if (result._tag === "Success") return; + const error = Cause.squash(result.cause); + Alert.alert( + "Could not rename thread", + error instanceof Error && error.message.trim().length > 0 + ? error.message + : "The thread could not be renamed.", + ); + }); + }; + + if (Platform.OS === "ios") { + Alert.prompt( + "Rename thread", + undefined, + (title) => commit(title ?? ""), + "plain-text", + thread.title, + ); + return; + } + showTextInputDialog({ + title: "Rename thread", + initialValue: thread.title, + confirmText: "Rename", + onConfirm: commit, + }); + }, + [updateThreadMetadata], + ); // Plan against the complete section so filtering does not change a move. const reorderPinnedMutation = useAtomCommand(threadEnvironment.reorderPin, { @@ -653,6 +699,7 @@ export function useThreadListActions(): { pinThread, unpinThread, moveThread, + renameThread, regenerateThreadTitle, }; } diff --git a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx index 11130b5454..de2d53ce8d 100644 --- a/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx +++ b/apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx @@ -171,6 +171,7 @@ function ThreadNavigationSidebarPane( pinThread, unpinThread, moveThread, + renameThread, regenerateThreadTitle, } = useThreadListActions(); const threadListV2Enabled = useThreadListV2Enabled(); @@ -961,6 +962,7 @@ function ThreadNavigationSidebarPane( onSelectThread={handleSelectThread} onDeleteThread={confirmDeleteThread} onArchiveThread={archiveThread} + onRenameThread={renameThread} onRegenerateThreadTitle={regenerateThreadTitle} titleRegenerationSupported={titleRegenerationEnvironmentIds.has(thread.environmentId)} settlementSupported={settlementEnvironmentIds.has(thread.environmentId)} @@ -1084,6 +1086,7 @@ function ThreadNavigationSidebarPane( fullSwipeWidth={props.width - 20} onArchiveThread={archiveThread} onDeleteThread={confirmDeleteThread} + onRenameThread={renameThread} onRegenerateThreadTitle={regenerateThreadTitle} titleRegenerationSupported={titleRegenerationEnvironmentIds.has(thread.environmentId)} onSelectThread={handleSelectThread} @@ -1128,6 +1131,7 @@ function ThreadNavigationSidebarPane( projectCwdByKey, projectTitleByProjectKey, regenerateThreadTitle, + renameThread, props.onNewThreadInProject, props.onNewThreadOnBranch, props.searchQuery, diff --git a/apps/mobile/src/features/threads/thread-list-items.tsx b/apps/mobile/src/features/threads/thread-list-items.tsx index fee738ef99..d9082e0e12 100644 --- a/apps/mobile/src/features/threads/thread-list-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-items.tsx @@ -457,6 +457,7 @@ export const PendingTaskListRow = memo(function PendingTaskListRow(props: { const THREAD_ROW_MENU_ACTIONS: MenuAction[] = [ { id: "archive", title: "Archive", image: "archivebox" }, + { id: "rename", title: "Rename", image: "square.and.pencil" }, { id: "delete", title: "Delete", image: "trash", attributes: { destructive: true } }, ]; @@ -479,6 +480,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { readonly onArchiveThread: (thread: EnvironmentThreadShell) => void; readonly onDeleteThread: (thread: EnvironmentThreadShell) => void; readonly onNewThreadOnBranch: (thread: EnvironmentThreadShell) => void; + readonly onRenameThread: (thread: EnvironmentThreadShell) => void; readonly onRegenerateThreadTitle: (thread: EnvironmentThreadShell) => void; readonly titleRegenerationSupported: boolean; readonly onSwipeableWillOpen: (methods: SwipeableMethods) => void; @@ -510,6 +512,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { onSelectThread, onArchiveThread, onDeleteThread, + onRenameThread, onRegenerateThreadTitle, onNewThreadOnBranch, } = props; @@ -554,6 +557,7 @@ export const ThreadListRow = memo(function ThreadListRow(props: { const handleDelete = useCallback(() => onDeleteThread(thread), [onDeleteThread, thread]); const handleArchive = useCallback(() => onArchiveThread(thread), [onArchiveThread, thread]); + const handleRename = useCallback(() => onRenameThread(thread), [onRenameThread, thread]); const handleRegenerateTitle = useCallback( () => onRegenerateThreadTitle(thread), [onRegenerateThreadTitle, thread], @@ -571,12 +575,13 @@ export const ThreadListRow = memo(function ThreadListRow(props: { ] : []), THREAD_ROW_MENU_ACTIONS[0]!, + THREAD_ROW_MENU_ACTIONS[1]!, { id: "copy-thread-id", title: "Copy thread ID", image: "doc.on.doc" }, ...buildThreadTitleRegenerationMenuItems({ supported: props.titleRegenerationSupported, isRegenerating: thread.titleRegeneration != null, }), - THREAD_ROW_MENU_ACTIONS[1]!, + THREAD_ROW_MENU_ACTIONS[2]!, ], [props.titleRegenerationSupported, thread.branch, thread.titleRegeneration], ); @@ -593,13 +598,14 @@ export const ThreadListRow = memo(function ThreadListRow(props: { ({ nativeEvent }: { readonly nativeEvent: { readonly event: string } }) => { if (nativeEvent.event === "new-thread-on-branch") onNewThreadOnBranch(thread); if (nativeEvent.event === "archive") handleArchive(); + if (nativeEvent.event === "rename") handleRename(); if (nativeEvent.event === "copy-thread-id") { copyTextWithHaptic(thread.id, { target: "thread-id" }); } if (nativeEvent.event === "regenerate-title") handleRegenerateTitle(); if (nativeEvent.event === "delete") handleDelete(); }, - [handleArchive, handleDelete, handleRegenerateTitle, onNewThreadOnBranch, thread], + [handleArchive, handleDelete, handleRegenerateTitle, handleRename, onNewThreadOnBranch, thread], ); const statusPill = effectiveStatus ? ( diff --git a/apps/mobile/src/features/threads/thread-list-v2-items.tsx b/apps/mobile/src/features/threads/thread-list-v2-items.tsx index e33f6f6c30..e02390e2b9 100644 --- a/apps/mobile/src/features/threads/thread-list-v2-items.tsx +++ b/apps/mobile/src/features/threads/thread-list-v2-items.tsx @@ -395,6 +395,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { readonly onSelectThread: (thread: EnvironmentThreadShell) => void; readonly onDeleteThread: (thread: EnvironmentThreadShell) => void; readonly onNewThreadOnBranch: (thread: EnvironmentThreadShell) => void; + readonly onRenameThread: (thread: EnvironmentThreadShell) => void; readonly onRegenerateThreadTitle: (thread: EnvironmentThreadShell) => void; readonly onSettleThread: (thread: EnvironmentThreadShell) => Promise; readonly onSnoozeThread: (thread: EnvironmentThreadShell, snoozedUntil: string) => void; @@ -437,6 +438,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { variant, onSelectThread, onDeleteThread, + onRenameThread, onRegenerateThreadTitle, onNewThreadOnBranch, onSettleThread, @@ -489,6 +491,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { settledTimestamp !== null ? relativeTime(settledTimestamp) : threadTimeLabel(thread); const handleDelete = useCallback(() => onDeleteThread(thread), [onDeleteThread, thread]); + const handleRename = useCallback(() => onRenameThread(thread), [onRenameThread, thread]); const handleRegenerateTitle = useCallback( () => onRegenerateThreadTitle(thread), [onRegenerateThreadTitle, thread], @@ -578,12 +581,14 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { variant, ], ); - const titleRegenerationMenuItems = useMemo( - () => - buildThreadTitleRegenerationMenuItems({ + const titleMenuItems = useMemo( + () => [ + { id: "rename", title: "Rename", image: "square.and.pencil" }, + ...buildThreadTitleRegenerationMenuItems({ supported: props.titleRegenerationSupported, isRegenerating: thread.titleRegeneration != null, }), + ], [props.titleRegenerationSupported, thread.titleRegeneration], ); const snoozableCardMenuActions = useMemo( @@ -596,19 +601,19 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { subactions: snoozePresetActions, }, ...arrangementMenuItems, - ...titleRegenerationMenuItems, + ...titleMenuItems, { id: "delete", title: "Delete", image: "trash", attributes: { destructive: true } }, ], - [arrangementMenuItems, snoozePresetActions, titleRegenerationMenuItems], + [arrangementMenuItems, snoozePresetActions, titleMenuItems], ); const cardMenuActions = useMemo( () => [ CARD_MENU_ACTIONS[0]!, ...arrangementMenuItems, - ...titleRegenerationMenuItems, + ...titleMenuItems, ...CARD_MENU_ACTIONS.slice(1), ], - [arrangementMenuItems, titleRegenerationMenuItems], + [arrangementMenuItems, titleMenuItems], ); const slimMenuActions = useMemo( () => [ @@ -616,23 +621,23 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { ...arrangementMenuItems.filter( (action) => action.id !== "move-up" && action.id !== "move-down", ), - ...titleRegenerationMenuItems, + ...titleMenuItems, SLIM_MENU_ACTIONS[1]!, ], - [arrangementMenuItems, titleRegenerationMenuItems], + [arrangementMenuItems, titleMenuItems], ); const snoozedMenuActions = useMemo( - () => [SNOOZED_MENU_ACTIONS[0]!, ...titleRegenerationMenuItems, SNOOZED_MENU_ACTIONS[1]!], - [titleRegenerationMenuItems], + () => [SNOOZED_MENU_ACTIONS[0]!, ...titleMenuItems, SNOOZED_MENU_ACTIONS[1]!], + [titleMenuItems], ); const legacyMenuActions = useMemo( () => [ LEGACY_MENU_ACTIONS[0]!, ...arrangementMenuItems, - ...titleRegenerationMenuItems, + ...titleMenuItems, LEGACY_MENU_ACTIONS[1]!, ], - [arrangementMenuItems, titleRegenerationMenuItems], + [arrangementMenuItems, titleMenuItems], ); const handleMenuAction = useCallback( ({ nativeEvent }: { readonly nativeEvent: { readonly event: string } }) => { @@ -646,6 +651,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { if (nativeEvent.event === "move-up") handleMoveUp(); if (nativeEvent.event === "move-down") handleMoveDown(); if (nativeEvent.event === "archive") handleArchive(); + if (nativeEvent.event === "rename") handleRename(); if (nativeEvent.event === "copy-thread-id") { copyTextWithHaptic(thread.id, { target: "thread-id" }); } @@ -668,6 +674,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: { handleArchive, handleDelete, handleRegenerateTitle, + handleRename, handleMoveDown, handleMoveUp, handlePin, diff --git a/apps/mobile/src/features/threads/thread-title-rename.test.ts b/apps/mobile/src/features/threads/thread-title-rename.test.ts new file mode 100644 index 0000000000..75bc5cdc41 --- /dev/null +++ b/apps/mobile/src/features/threads/thread-title-rename.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { resolveThreadTitleRename } from "./thread-title-rename"; + +describe("resolveThreadTitleRename", () => { + it("trims a changed title", () => { + expect(resolveThreadTitleRename({ title: " New title ", originalTitle: "Old" })).toEqual({ + action: "rename", + title: "New title", + }); + }); + + it("rejects empty and unchanged titles", () => { + expect(resolveThreadTitleRename({ title: " ", originalTitle: "Old" })).toEqual({ + action: "reject-empty", + }); + expect(resolveThreadTitleRename({ title: " Old ", originalTitle: "Old" })).toEqual({ + action: "noop", + }); + }); +}); diff --git a/apps/mobile/src/features/threads/thread-title-rename.ts b/apps/mobile/src/features/threads/thread-title-rename.ts new file mode 100644 index 0000000000..23f3e449ef --- /dev/null +++ b/apps/mobile/src/features/threads/thread-title-rename.ts @@ -0,0 +1,14 @@ +export type ThreadTitleRenameResolution = + | { readonly action: "rename"; readonly title: string } + | { readonly action: "noop" } + | { readonly action: "reject-empty" }; + +export function resolveThreadTitleRename(input: { + readonly title: string; + readonly originalTitle: string; +}): ThreadTitleRenameResolution { + const title = input.title.trim(); + if (title.length === 0) return { action: "reject-empty" }; + if (title === input.originalTitle) return { action: "noop" }; + return { action: "rename", title }; +} From fe8a8e51df3869b2c243c0b533f5f13ca2c0747b Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:55:14 -0600 Subject: [PATCH 095/104] fix(mobile): keep chat rows aligned after measurement (#777) --- apps/mobile/src/features/threads/ThreadFeed.tsx | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/apps/mobile/src/features/threads/ThreadFeed.tsx b/apps/mobile/src/features/threads/ThreadFeed.tsx index 2d5863aa41..0ac308c1a3 100644 --- a/apps/mobile/src/features/threads/ThreadFeed.tsx +++ b/apps/mobile/src/features/threads/ThreadFeed.tsx @@ -91,7 +91,7 @@ import { isPdfFile } from "../../lib/filePreview"; import { flattenThemeColor } from "../../lib/mobileTheme"; import { PresentationSource } from "../../components/NativePresentation"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import Animated, { FadeIn, LinearTransition, type SharedValue } from "react-native-reanimated"; +import Animated, { FadeIn, type SharedValue } from "react-native-reanimated"; import { useUniwindTheme } from "../../lib/useUniwindTheme"; import { IOS_NAV_BAR_HEIGHT } from "../../lib/layoutMetrics"; import { useFontFamily } from "../../lib/useFontFamily"; @@ -222,8 +222,6 @@ function formatMessageTime(input: string): string { // Fixed heights mirror renderFeedEntry's classNames and are only used while // text fits at the current font settings. Larger accessibility text is measured. const TURN_FOLD_HEIGHT = 42; // min-h-11 (38.5) + mb-1 (3.5), with the mobile 14px rem -const THREAD_FEED_LAYOUT_TRANSITION = LinearTransition.duration(THREAD_DISCLOSURE_TRANSITION_MS); -const THREAD_FEED_IMMEDIATE_TRANSITION = LinearTransition.duration(0); // Let neighboring rows move out of the new rows' space before showing their text. const THREAD_FEED_DISCLOSURE_ENTER_TRANSITION = FadeIn.delay( THREAD_DISCLOSURE_TRANSITION_MS, @@ -2887,17 +2885,8 @@ export const ThreadFeed = memo(function ThreadFeed(props: ThreadFeedProps) { entry.type === "message" ? `message:${entry.message.role}` : entry.type } getFixedItemSize={getFixedItemSize} - // Android can retain stale native row positions when layout transitions - // race the measurements arriving during sync, even with duration 0. - // Keep its rows on LegendList's non-animated positioning path. On iOS, - // keep a transition installed between disclosures so containers don't remount. - itemLayoutAnimation={ - Platform.OS === "android" - ? undefined - : disclosureToggleSettling - ? THREAD_FEED_LAYOUT_TRANSITION - : THREAD_FEED_IMMEDIATE_TRANSITION - } + // Virtualized rows must move with their measurements. Native layout + // transitions can retain stale positions during sync, even at duration 0. onItemSizeChanged={handleItemSizeChanged} // Measure rows well before they scroll into view so estimate→actual // corrections land offscreen instead of under the user's finger. From afdbde5b306c8f4718dd09971aa633f33fb7d49b Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Thu, 24 Sep 2026 23:56:47 -0600 Subject: [PATCH 096/104] feat(web): add file context actions across diff and browser (#836) --- apps/web/src/components/DiffPanel.tsx | 75 +++++ .../src/components/chat/ChangedFilesTree.tsx | 47 +++- .../src/components/chat/MessagesTimeline.tsx | 30 ++ .../src/components/files/FileBrowserPanel.tsx | 25 +- apps/web/src/fileContextMenu.test.ts | 155 +++++++++++ apps/web/src/fileContextMenu.ts | 262 ++++++++++++++++++ 6 files changed, 591 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/fileContextMenu.test.ts create mode 100644 apps/web/src/fileContextMenu.ts diff --git a/apps/web/src/components/DiffPanel.tsx b/apps/web/src/components/DiffPanel.tsx index 547856a426..a0fa23d5aa 100644 --- a/apps/web/src/components/DiffPanel.tsx +++ b/apps/web/src/components/DiffPanel.tsx @@ -26,6 +26,7 @@ import * as Schema from "effect/Schema"; import * as DateTime from "effect/DateTime"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useOpenInPreferredEditor } from "../editorPreferences"; +import { useFileContextMenuHandler } from "../fileContextMenu"; import { type DraftId } from "../composerDraftStore"; import { openDiffFilePrimaryAction } from "../diffFileActions"; import { useCheckpointDiff } from "~/lib/checkpointDiffState"; @@ -171,6 +172,10 @@ export default function DiffPanel({ const serverConfig = useAtomValue( serverEnvironment.configValueAtom(activeThread?.environmentId ?? null), ); + const onFileContextMenu = useFileContextMenuHandler( + activeThread?.environmentId ?? null, + activeCwd, + ); const openInPreferredEditor = useOpenInPreferredEditor( activeThread?.environmentId ?? null, serverConfig?.availableEditors ?? [], @@ -496,6 +501,29 @@ export default function DiffPanel({ }), [collapsedDiffFileKeys, renderableFileEntries, lazySource, readyFilePaths], ); + const showDiffFileContextMenu = useCallback( + (filePath: string, position: { clientX: number; clientY: number }) => { + const matching = codeViewFiles.filter((file) => file.filePath === filePath); + if (matching.length === 0) return false; + return onFileContextMenu( + { + environmentId: activeThread?.environmentId ?? null, + filePath, + fileExists: matching.some((file) => file.fileDiff.type !== "deleted"), + workspaceRoot: activeCwd, + repositoryRoot: activeRepositoryRoot, + }, + position, + ); + }, + [ + activeCwd, + activeRepositoryRoot, + activeThread?.environmentId, + codeViewFiles, + onFileContextMenu, + ], + ); const diffFileKeys = useMemo( () => renderableFileEntries.map((file) => file.fileKey), [renderableFileEntries], @@ -1073,6 +1101,53 @@ export default function DiffPanel({ ); if (file) toggleDiffFileCollapsed(file.fileKey); }} + onContextMenuCapture={(event) => { + const path = event.nativeEvent.composedPath?.() ?? []; + if ( + path.some( + (node) => + node instanceof HTMLButtonElement || node instanceof HTMLAnchorElement, + ) + ) + return; + const title = path.find( + (node): node is HTMLElement => + node instanceof HTMLElement && node.hasAttribute("data-title"), + ); + const header = path.find( + (node): node is HTMLElement => + node instanceof HTMLElement && node.hasAttribute("data-diffs-header"), + ); + const filePath = + title?.textContent ?? header?.querySelector("[data-title]")?.textContent; + if (!filePath) return; + const rect = + title?.getBoundingClientRect() ?? header?.getBoundingClientRect(); + if ( + showDiffFileContextMenu(filePath, { + clientX: event.clientX || rect?.left || 0, + clientY: event.clientY || rect?.bottom || 0, + }) + ) + event.preventDefault(); + }} + onKeyDownCapture={(event) => { + if (event.key !== "ContextMenu" && !(event.shiftKey && event.key === "F10")) + return; + const target = event.target; + if (!(target instanceof HTMLElement)) return; + const header = target.closest("[data-diffs-header]"); + const filePath = header?.querySelector("[data-title]")?.textContent; + if (!filePath || !header) return; + const rect = header.getBoundingClientRect(); + if ( + showDiffFileContextMenu(filePath, { + clientX: rect.left, + clientY: rect.bottom, + }) + ) + event.preventDefault(); + }} > = {}; +/** Opens the OS-level context menu for a changed file (reveal in file manager, open in editor). */ +export type ChangedFileContextMenuHandler = ( + filePath: string, + position: { readonly clientX: number; readonly clientY: number }, + fileExists: boolean, +) => boolean; + export const ChangedFilesCard = memo(function ChangedFilesCard(props: { turnId: TurnId; files: ReadonlyArray; @@ -30,6 +37,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { resolvedTheme: "light" | "dark"; onToggleAllDirectories: () => void; onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void; + onFileContextMenu?: ChangedFileContextMenuHandler | undefined; }) { const { turnId, @@ -38,6 +46,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { resolvedTheme, onToggleAllDirectories, onOpenTurnDiff, + onFileContextMenu, } = props; const summaryStat = useMemo(() => summarizeTurnDiffStats(files), [files]); const hasDirectories = files.some((file) => /[/\\]/.test(file.path)); @@ -118,6 +127,7 @@ export const ChangedFilesCard = memo(function ChangedFilesCard(props: { allDirectoriesExpanded={allDirectoriesExpanded} resolvedTheme={resolvedTheme} onOpenTurnDiff={onOpenTurnDiff} + onFileContextMenu={onFileContextMenu} />
); @@ -129,9 +139,26 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: { allDirectoriesExpanded: boolean; resolvedTheme: "light" | "dark"; onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void; + onFileContextMenu?: ChangedFileContextMenuHandler | undefined; }) { - const { files, allDirectoriesExpanded, onOpenTurnDiff, resolvedTheme, turnId } = props; + const { + files, + allDirectoriesExpanded, + onOpenTurnDiff, + resolvedTheme, + turnId, + onFileContextMenu, + } = props; const treeNodes = useMemo(() => buildTurnDiffTree(files), [files]); + const deletedPaths = useMemo( + () => + new Set( + files + .filter((file) => /^(deleted|removed|d)$/iu.test(file.kind)) + .map((file) => file.path.replaceAll("\\", "/")), + ), + [files], + ); const directoryPathsKey = useMemo( () => collectDirectoryPaths(treeNodes).join("\u0000"), [treeNodes], @@ -215,6 +242,24 @@ export const ChangedFilesTree = memo(function ChangedFilesTree(props: { className="group flex w-full items-center gap-2 rounded-md py-1.5 pr-2 text-left transition-colors hover:bg-accent/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background" style={{ paddingLeft: `${leftPadding}px` }} onClick={() => onOpenTurnDiff(turnId, node.path)} + onContextMenu={ + onFileContextMenu + ? (event) => { + const rect = event.currentTarget.getBoundingClientRect(); + if ( + onFileContextMenu( + node.path, + { + clientX: event.clientX || rect.left, + clientY: event.clientY || rect.bottom, + }, + !deletedPaths.has(node.path), + ) + ) + event.preventDefault(); + } + : undefined + } > {hasDirectoryNodes || depth > 0 ? (
)} - {listQuery.error && entries.length > 0 ? ( + {listQuery.error && shownCount > 0 ? (
{listQuery.error} Showing the last pull requests loaded. @@ -343,23 +349,24 @@ export function DeviceHostsSettings(props: { Cancel
- {checks[editing.id]?.pending ? ( - - - Checking connection… - - ) : null} - {checks[editing.id]?.platforms ? ( - - ) : null} - {checks[editing.id]?.error ? ( -

- {checks[editing.id]?.error} -

- ) : null} + {targets.map((target) => { + const result = + environmentChecks[deviceHostChecksKey(editing, targets)]?.[ + target.environmentId + ]; + if (!result) return null; + return ( +
+ {target.label}: + {result.status === "pending" ? "Checking…" : null} + {result.status === "local" ? "Already available locally" : null} + {result.status === "failed" ? result.error : null} + {result.status === "connected" ? ( + + ) : null} +
+ ); + })} ) : null} diff --git a/apps/web/src/components/settings/IntegrationsSettings.test.tsx b/apps/web/src/components/settings/IntegrationsSettings.test.tsx index 8b1a4585f7..17d18f1ad0 100644 --- a/apps/web/src/components/settings/IntegrationsSettings.test.tsx +++ b/apps/web/src/components/settings/IntegrationsSettings.test.tsx @@ -57,6 +57,12 @@ vi.mock("./SettingsScopeContext", () => ({ } : null, connectedEnvironments: selectedDeviceEnvironment.aggregate ? [{}, {}] : [], + environments: selectedDeviceEnvironment.aggregate + ? [ + { environmentId: "remote", label: "Selected remote", connection: { phase: "connected" } }, + { environmentId: "other", label: "Other", connection: { phase: "connected" } }, + ] + : [], targets: [], }), useOptionalSettingsScope: () => null, diff --git a/apps/web/src/components/settings/deviceHostConnectionChecks.test.ts b/apps/web/src/components/settings/deviceHostConnectionChecks.test.ts new file mode 100644 index 0000000000..76fe0769a0 --- /dev/null +++ b/apps/web/src/components/settings/deviceHostConnectionChecks.test.ts @@ -0,0 +1,96 @@ +import * as Option from "effect/Option"; +import { describe, expect, it } from "vite-plus/test"; +import { EnvironmentId, type DeviceHostSummary } from "@t3tools/contracts"; +import { + checkDeviceHostConnections, + parseDeviceHostDraft, + deviceHostConnectionKey, + deviceHostChecksKey, + type DeviceHostCheck, +} from "./deviceHostConnectionChecks"; + +const host = { id: "mac", label: "Mac mini", target: "user@mac" }; +const ids = ["a", "b", "c", "d"].map((id) => EnvironmentId.make(id)); +const targets = ids.map((environmentId, index) => ({ + environmentId, + label: environmentId, + connected: index !== 3, +})); +const summary: DeviceHostSummary = { + id: "mac", + label: "Mac mini", + kind: "ssh", + platforms: [{ platform: "ios", available: true }], + hubInstalled: false, + agentDeviceInstalled: false, +}; + +describe("device host connection checks", () => { + it("starts all connected environments and retains success, local, failure, and offline results", async () => { + const calls: string[] = []; + const pending = new Map< + string, + { resolve: (value: DeviceHostSummary) => void; reject: (error: Error) => void } + >(); + const results = new Map(); + const run = checkDeviceHostConnections( + targets, + host, + (environmentId) => { + calls.push(environmentId); + return new Promise((resolve, reject) => pending.set(environmentId, { resolve, reject })); + }, + (environmentId, result) => results.set(environmentId, result), + ); + expect(calls).toEqual(ids.slice(0, 3)); + expect(results.get(ids[0]!)).toEqual({ status: "pending" }); + pending.get(ids[0]!)!.resolve(summary); + pending.get(ids[1]!)!.resolve({ ...summary, id: "local", kind: "local" }); + pending.get(ids[2]!)!.reject(new Error("SSH key rejected")); + await run; + expect([...results.values()]).toEqual([ + { status: "connected", platforms: summary.platforms }, + { status: "local" }, + { status: "failed", error: "SSH key rejected" }, + { status: "failed", error: "Environment disconnected" }, + ]); + }); + + it("does not reuse results after editing a destination or SSH options", () => { + const key = deviceHostConnectionKey(host); + for (const changed of [ + { ...host, target: "other" }, + { ...host, port: 2222 }, + { ...host, identityFile: "~/.ssh/other" }, + ]) { + expect(deviceHostConnectionKey(changed)).not.toBe(key); + } + expect( + deviceHostConnectionKey({ ...host, id: "another-environment-id", label: "Renamed" }), + ).toBe(key); + }); + it("does not reuse results when selected environments or connectivity change", () => { + const key = deviceHostChecksKey(host, targets); + expect(deviceHostChecksKey(host, targets.slice(0, 2))).not.toBe(key); + expect( + deviceHostChecksKey( + host, + targets.map((target) => ({ ...target, connected: true })), + ), + ).not.toBe(key); + }); + it("validates SSH targets and normalizes optional identity files through the host contract", () => { + for (const target of ["-invalid", "user@bad host", " "]) { + expect(parseDeviceHostDraft({ ...host, target })._tag).toBe("None"); + } + for (const port of [0, 65536, 1.5]) { + expect(parseDeviceHostDraft({ ...host, port })._tag).toBe("None"); + } + expect(parseDeviceHostDraft({ ...host, target: " user@mac ", identityFile: " " })).toEqual( + Option.some(host), + ); + expect(parseDeviceHostDraft({ ...host, identityFile: " ~/.ssh/device " })).toEqual( + Option.some({ ...host, identityFile: "~/.ssh/device" }), + ); + }); +}); diff --git a/apps/web/src/components/settings/deviceHostConnectionChecks.ts b/apps/web/src/components/settings/deviceHostConnectionChecks.ts new file mode 100644 index 0000000000..9a8179e175 --- /dev/null +++ b/apps/web/src/components/settings/deviceHostConnectionChecks.ts @@ -0,0 +1,71 @@ +import { + type DeviceHostSummary, + type DevicePlatformAvailability, + type EnvironmentId, + SshDeviceHostConfig, +} from "@t3tools/contracts"; +import * as Schema from "effect/Schema"; + +export interface DeviceHostCheckTarget { + environmentId: EnvironmentId; + label: string; + connected: boolean; +} +export type DeviceHostCheck = + | { status: "pending" } + | { status: "local" } + | { status: "connected"; platforms: ReadonlyArray } + | { status: "failed"; error: string }; + +const decodeDeviceHostDraft = Schema.decodeUnknownOption(SshDeviceHostConfig); + +export function parseDeviceHostDraft(host: SshDeviceHostConfig) { + const { identityFile, ...rest } = host; + return decodeDeviceHostDraft({ + ...rest, + ...(identityFile?.trim() ? { identityFile: identityFile.trim() } : {}), + }); +} + +export function deviceHostConnectionKey(host: SshDeviceHostConfig) { + return JSON.stringify([host.target.trim(), host.port, host.identityFile?.trim() || undefined]); +} + +export function deviceHostChecksKey( + host: SshDeviceHostConfig, + targets: ReadonlyArray, +) { + return JSON.stringify([ + deviceHostConnectionKey(host), + targets.map((target) => [target.environmentId, target.connected]), + ]); +} + +/** Each environment settles independently so one failure cannot hide the other results. */ +export async function checkDeviceHostConnections( + targets: ReadonlyArray, + host: SshDeviceHostConfig, + probe: (environmentId: EnvironmentId, host: SshDeviceHostConfig) => Promise, + report: (environmentId: EnvironmentId, result: DeviceHostCheck) => void, +) { + await Promise.all( + targets.map(async (target) => { + report(target.environmentId, { status: "pending" }); + try { + if (!target.connected) throw new Error("Environment disconnected"); + const result = await probe(target.environmentId, host); + report( + target.environmentId, + result.kind === "local" + ? { status: "local" } + : { status: "connected", platforms: result.platforms }, + ); + } catch (error) { + report(target.environmentId, { + status: "failed", + error: error instanceof Error ? error.message : String(error), + }); + } + }), + ); +} diff --git a/apps/web/src/components/settings/useHostConnectionChecks.ts b/apps/web/src/components/settings/useHostConnectionChecks.ts new file mode 100644 index 0000000000..f7ba43d275 --- /dev/null +++ b/apps/web/src/components/settings/useHostConnectionChecks.ts @@ -0,0 +1,46 @@ +import { useRef, useState } from "react"; +import * as Cause from "effect/Cause"; +import type { SshDeviceHostConfig } from "@t3tools/contracts"; +import { deviceEnvironment } from "../../state/device"; +import { useAtomCommand } from "../../state/use-atom-command"; +import { + checkDeviceHostConnections, + deviceHostChecksKey, + type DeviceHostCheck, + type DeviceHostCheckTarget, +} from "./deviceHostConnectionChecks"; + +export function useHostConnectionChecks(targets: ReadonlyArray) { + const test = useAtomCommand(deviceEnvironment.testHost, { reportFailure: false }); + const [checks, setChecks] = useState>>({}); + const running = useRef(new Set()); + const testConnection = async (host: SshDeviceHostConfig) => { + const key = deviceHostChecksKey(host, targets); + if (running.current.has(key)) return; + running.current.add(key); + setChecks((current) => ({ ...current, [key]: {} })); + const results: Record = {}; + try { + await checkDeviceHostConnections( + targets, + host, + async (environmentId, input) => { + const result = await test({ environmentId, input }); + if (result._tag === "Failure") throw new Error(Cause.pretty(result.cause)); + return result.value; + }, + (environmentId, result) => { + results[environmentId] = result; + setChecks((current) => ({ + ...current, + [key]: { ...current[key], [environmentId]: result }, + })); + }, + ); + return results; + } finally { + running.current.delete(key); + } + }; + return { checks, testConnection }; +} diff --git a/docs/user/devices.md b/docs/user/devices.md index fb26486e96..60409a1025 100644 --- a/docs/user/devices.md +++ b/docs/user/devices.md @@ -82,7 +82,11 @@ so use the SSH configuration and keys available there. Password prompts are not supported. **Test connection** checks SSH, Node, npm, and platform tools without installing -anything. The first device listing installs pinned device tools on the host. +anything. When adding or editing a host, the check reports a result for each environment +selected in Settings; saving changes the host list on the environment shown in +the Devices section. An SSH alias that resolves to the environment server's own +machine is skipped, unless it uses a forwarded port or SSH proxy. The first +device listing installs pinned device tools on the host. Node 22 or newer and npm must be available to non-interactive SSH commands. Pylon checks common Homebrew and Android SDK locations; custom installations need the appropriate PATH and ANDROID_HOME on the host. From 3b62ac4e3e7a5f73c90df1cb334a0dd557acab47 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Fri, 25 Sep 2026 00:18:35 -0600 Subject: [PATCH 103/104] fix(deps): update desktop packaging and security parents (#771) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(deps): update desktop packaging and security parents * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record p… * feat(web): filter sidebar from thread menu (#821) * feat(web): add bounded route history shortcuts on desktop (#803) * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(… * fix(web): add local dropped folders as path chips (#818) * feat(mobile): search projects when starting a task (#796) * feat(mobile): favorite models in thread settings (#790) * test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture * fix(desktop): keep SnapShot shortcut helper out of Dock (#778) * fix(mobile): wait for deep-link thread hydration (#764) * fix(relay): stale Relay workers no longer flood a reopened thread (#852) * fix(relay): stop replaying stale Relay workers into reopened threads The startup receipt scan adopts every Relay MCP tool call ever recorded, so the first boot after the worker bridge shipped bound 284 long-finished jobs across 207 old turns. None could be observed, and each one published an idle "Relay observer unavailable" row into its original turn, dated now -- a wall of stale subagent cards on reopening the thread. An outage row now only explains a worker the thread already started, and an announced outage pushes its recheck back out instead of spawning the Relay CLI on every sweep for the life of the process. * fix(relay): clear the stale Relay outage rows already written The bridge no longer announces a worker it never started, but every projection that booted the earlier build still holds the idle rows it wrote. Drop the outage rows whose task has no start in the same thread, leaving the ones that explain a worker the thread really ran. * fix(relay): retire adopted jobs Relay reports as gone An outage that had already been announced returned before pushing its recheck back out, so every binding kept a Relay CLI spawn on each sweep. Adopted history made that 284 spawns a sweep on one projection. Push the recheck out for a worker that may still return, and retire a binding outright once Relay itself reports the job gone and nothing about it ever reached the thread. Absent Relay says nothing about the job, so that case keeps the binding and only slows the recheck. The four copies of the teardown are now one helper. * fix(relay): type the test engine helper against the SqlClient service * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(relay): retire a lost job for good instead of re-probing every boot Retirement only lived in memory, so every job the environment had ever run was adopted and re-probed on each start -- a cost that grows with history and never settles. Record the retirement on the binding row itself. The activity upserts on its id, so this writes no new row and stays out of the transcript, and boot skips the binding unless an activation was recorded after the watermark. A resume outranks the retirement and wakes the worker, then answers to the ordinary rules again. * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762… * fix(mobile): keep ordinary offline outbox retries out of warnings (#783) * fix(mobile): keep ordinary offline outbox retries out of warnings * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposi… * fix(web): respect panel motion and nested timeline scrolling (#750) Adapt T3 Code sources da6a85b1365993d0ff2a79698cd82498de247d8a, 7c2702d68ae2e10fa9d3a08c996ed47c393dcffd, and 68607c5a99147710b92228e0642f6c0a642e32a6 for Pylon composer and context controls. * fix(preview): prefer visible browser for new leases (#767) * fix(preview): preserve picking and align animated surfaces (#839) * fix(desktop): keep preview picking active across subframe navigation (#9741) Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit 1ab2dfb5a7bd2996f79407b5d02cae6132a7626c) * fix(web): keep browser pages aligned during panel animations (#12329) (cherry picked from commit 4cc984fedc33184c0a0fb67ea17fb02902ae2224) * fix(preview): preserve picking and align animated surfaces Keep element picking armed across subframe navigation, observe inline preview panel movement, and use the shared resize lifecycle for the sidebar rail. --------- Co-authored-by: Simone Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Julius Marminge * feat(mobile): rename threads from list menus (#786) * feat(mobile): rename threads from list menus * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider r… * fix(mobile): keep chat rows aligned after measurement (#777) * feat(web): add file context actions across diff and browser (#836) * feat(client): preview thread lifecycle actions until shell receipts (#819) * feat(web): choose themes from command palette (#837) * feat(web): choose themes from command palette * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider… * fix(mobile): recover from screen render failures (#768) * fix(marketing): update Astro image dependency chain (#774) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari Co-authored-by: Simone Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> --- apps/desktop/package.json | 4 +- .../desktop/src/updates/updatesTestHarness.ts | 31 ++++++++- apps/marketing/package.json | 2 +- apps/server/package.json | 2 +- pnpm-lock.yaml | 69 +++++++++---------- pnpm-workspace.yaml | 4 +- scripts/build-desktop-artifact.ts | 6 +- scripts/package.json | 2 +- 8 files changed, 75 insertions(+), 45 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index b9ed3e8e43..46df2062ba 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -24,9 +24,9 @@ "@t3tools/tailscale": "workspace:*", "dbus-next": "0.10.2", "effect": "catalog:", - "electron": "44.1.0", + "electron": "44.4.2", "electron-store": "^8.2.0", - "electron-updater": "^6.6.2", + "electron-updater": "^6.8.9", "ffi-rs": "1.3.2", "playwright-core": "1.60.0", "react-grab": "^0.1.32" diff --git a/apps/desktop/src/updates/updatesTestHarness.ts b/apps/desktop/src/updates/updatesTestHarness.ts index ebef9001a7..f85c5d1058 100644 --- a/apps/desktop/src/updates/updatesTestHarness.ts +++ b/apps/desktop/src/updates/updatesTestHarness.ts @@ -3,6 +3,7 @@ import type { DesktopUpdateState } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; +import * as Config from "effect/Config"; import * as DesktopBackendPool from "../backend/DesktopBackendPool.ts"; import * as DesktopConfig from "../app/DesktopConfig.ts"; @@ -34,7 +35,33 @@ export interface UpdatesHarnessOptions { readonly appVersion?: string; } -export function makeHarness(options: UpdatesHarnessOptions = {}) { +type UpdatesHarnessLayer = Layer.Layer< + | DesktopAppSettings.DesktopAppSettings + | DesktopBackendPool.DesktopBackendPool + | DesktopEnvironment.DesktopEnvironment + | DesktopState.DesktopState + | DesktopUpdates.DesktopUpdates + | ElectronUpdater.ElectronUpdater + | ElectronWindow.ElectronWindow + | NodeServices.NodeServices, + Config.ConfigError +>; + +interface UpdatesHarness { + readonly layer: UpdatesHarnessLayer; + readonly checkCount: () => number; + readonly quitAndInstalls: () => number; + readonly installSteps: string[]; + readonly downloadCount: () => number; + readonly feedUrls: () => ReadonlyArray; + readonly allowPrerelease: () => boolean; + readonly fullChangelog: () => boolean; + readonly listenerCount: () => number; + readonly sentStates: DesktopUpdateState[]; + readonly emit: (eventName: string, payload?: unknown) => void; +} + +export function makeHarness(options: UpdatesHarnessOptions = {}): UpdatesHarness { let checkCount = 0; let quitAndInstallCount = 0; let downloadCount = 0; @@ -172,7 +199,7 @@ export function makeHarness(options: UpdatesHarnessOptions = {}) { const settingsLayer = DesktopAppSettings.layer; - const layer = DesktopUpdates.layer.pipe( + const layer: UpdatesHarnessLayer = DesktopUpdates.layer.pipe( Layer.provideMerge(updaterLayer), Layer.provideMerge(windowLayer), Layer.provideMerge(backendLayer), diff --git a/apps/marketing/package.json b/apps/marketing/package.json index df9c33d64a..a9a04b70b7 100644 --- a/apps/marketing/package.json +++ b/apps/marketing/package.json @@ -16,7 +16,7 @@ "sharp": "0.35.4" }, "devDependencies": { - "@astrojs/check": "^0.9.7", + "@astrojs/check": "^0.9.10", "@vercel/config": "^0.3.0", "typescript": "~6.0.3" } diff --git a/apps/server/package.json b/apps/server/package.json index e48d2dd3f5..e55f206c24 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -22,7 +22,7 @@ "test": "vp test run" }, "dependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.3.260", + "@anthropic-ai/claude-agent-sdk": "^0.3.276", "@effect/platform-bun": "catalog:", "@effect/platform-node": "catalog:", "@effect/platform-node-shared": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d5ec3a82b..273929f1eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -141,7 +141,7 @@ importers: dependencies: '@clerk/electron': specifier: 0.0.42 - version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/electron-passkeys': specifier: 0.0.3 version: 0.0.3 @@ -176,14 +176,14 @@ importers: specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2) electron: - specifier: 44.1.0 - version: 44.1.0 + specifier: 44.4.2 + version: 44.4.2 electron-store: specifier: ^8.2.0 version: 8.2.0 electron-updater: - specifier: ^6.6.2 - version: 6.8.3 + specifier: ^6.8.9 + version: 6.8.9 ffi-rs: specifier: 1.3.2 version: 1.3.2 @@ -226,8 +226,8 @@ importers: version: 0.35.4(@types/node@24.12.4) devDependencies: '@astrojs/check': - specifier: ^0.9.7 - version: 0.9.9(prettier@3.8.3)(typescript@6.0.3) + specifier: ^0.9.10 + version: 0.9.10(prettier@3.8.3)(typescript@6.0.3) '@vercel/config': specifier: ^0.3.0 version: 0.3.0 @@ -512,7 +512,7 @@ importers: apps/server: dependencies: '@anthropic-ai/claude-agent-sdk': - specifier: ^0.3.260 + specifier: ^0.3.276 version: 0.3.278(@anthropic-ai/sdk@0.93.0(zod@4.6.5))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3) '@effect/platform-bun': specifier: 4.0.0-rc.112 @@ -622,7 +622,7 @@ importers: version: 1.5.0(@types/react@19.2.16)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/electron': specifier: 0.0.42 - version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + version: 0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/react': specifier: 6.15.2 version: 6.15.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) @@ -1054,8 +1054,8 @@ importers: specifier: 4.0.0-rc.112 version: 4.0.0-rc.112(bufferutil@4.1.0)(effect@4.0.0-rc.112(patch_hash=8bef799f35729cf3465eb428196f617b434a7c9f658d56acb2874d74b21b98b2))(redis@6.2.1)(utf-8-validate@6.0.6) '@electron/asar': - specifier: ^3.4.1 - version: 3.4.1 + specifier: ^4.3.0 + version: 4.3.0 '@electron/osx-sign': specifier: 2.7.0 version: 2.7.0 @@ -1151,8 +1151,8 @@ packages: zod: optional: true - '@astrojs/check@0.9.9': - resolution: {integrity: sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg==} + '@astrojs/check@0.9.10': + resolution: {integrity: sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==} hasBin: true peerDependencies: typescript: ^5.0.0 || ^6.0.0 @@ -2245,6 +2245,11 @@ packages: engines: {node: '>=10.12.0'} hasBin: true + '@electron/asar@4.3.0': + resolution: {integrity: sha512-k/FFC/NQoTykGBi/Ga4L4KorsAKDdkK0LfNVG90eUG6vPVpJaL3iR9V1ZLbzBAF3QpojK5DcaGOdQOheKZv4JQ==} + engines: {node: '>=22.12.0'} + hasBin: true + '@electron/fuses@1.8.0': resolution: {integrity: sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==} hasBin: true @@ -6387,10 +6392,6 @@ packages: resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} - builder-util-runtime@9.5.1: - resolution: {integrity: sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==} - engines: {node: '>=12.0.0'} - builder-util-runtime@9.7.0: resolution: {integrity: sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==} engines: {node: '>=12.0.0'} @@ -7129,15 +7130,15 @@ packages: electron-to-chromium@1.5.364: resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==} - electron-updater@6.8.3: - resolution: {integrity: sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==} + electron-updater@6.8.9: + resolution: {integrity: sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig==} electron-winstaller@5.4.0: resolution: {integrity: sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==} engines: {node: '>=8.0.0'} - electron@44.1.0: - resolution: {integrity: sha512-kmLg8axOg22DC3fXx5NCwBYW8fx0rK2zzJ3Tf67GjNCkxfoxEcd+yo0QfDjlBtHJs3xeA8fTg64b6iVzFTVErg==} + electron@44.4.2: + resolution: {integrity: sha512-872t/+8M8TyribWm2cYpRwHY2aDlyDHOn+CHnhN9kHQStVW8pAijIhFolYA/qQyDdri28Xp8Mi3HEj5kFvajCw==} engines: {node: '>= 22.12.0'} hasBin: true @@ -11421,13 +11422,13 @@ snapshots: optionalDependencies: zod: 4.6.5 - '@astrojs/check@0.9.9(prettier@3.8.3)(typescript@6.0.3)': + '@astrojs/check@0.9.10(prettier@3.8.3)(typescript@6.0.3)': dependencies: '@astrojs/language-server': 2.16.10(prettier@3.8.3)(typescript@6.0.3) chokidar: 4.0.3 kleur: 4.1.5 typescript: 6.0.3 - yargs: 17.7.2 + yargs: 18.1.0 transitivePeerDependencies: - prettier - prettier-plugin-astro @@ -12408,12 +12409,12 @@ snapshots: '@clerk/electron-passkeys-win32-arm64-msvc': 0.0.3 '@clerk/electron-passkeys-win32-x64-msvc': 0.0.3 - '@clerk/electron@0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.1.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + '@clerk/electron@0.0.42(@clerk/electron-passkeys@0.0.3)(electron-store@8.2.0)(electron@44.4.2)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': dependencies: '@clerk/clerk-js': 6.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/react': 6.15.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6) '@clerk/shared': 4.31.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6) - electron: 44.1.0 + electron: 44.4.2 react: 19.2.6 tslib: 2.8.1 optionalDependencies: @@ -12753,6 +12754,11 @@ snapshots: glob: 7.2.3 minimatch: 3.1.5 + '@electron/asar@4.3.0': + dependencies: + glob: 13.0.6 + minimatch: 10.2.5 + '@electron/fuses@1.8.0': dependencies: chalk: 4.1.2 @@ -16927,13 +16933,6 @@ snapshots: node-gyp-build: 4.8.4 optional: true - builder-util-runtime@9.5.1: - dependencies: - debug: 4.4.3 - sax: 1.6.0 - transitivePeerDependencies: - - supports-color - builder-util-runtime@9.7.0: dependencies: debug: 4.4.3 @@ -17569,9 +17568,9 @@ snapshots: electron-to-chromium@1.5.364: {} - electron-updater@6.8.3: + electron-updater@6.8.9: dependencies: - builder-util-runtime: 9.5.1 + builder-util-runtime: 9.7.0 fs-extra: 10.1.0 js-yaml: 4.2.0 lazy-val: 1.0.5 @@ -17594,7 +17593,7 @@ snapshots: transitivePeerDependencies: - supports-color - electron@44.1.0: + electron@44.4.2: dependencies: '@electron-internal/extract-zip': 1.0.5 '@electron/get': 5.1.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4b33e0c506..97f227be43 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -91,8 +91,8 @@ minimumReleaseAgeExclude: - expo-updates@57.0.19 - expo@57.0.18 - expo-widgets@57.0.15 - - electron@44.1.0 - - "@anthropic-ai/claude-agent-sdk@0.3.260" + - electron@44.4.2 + - "@anthropic-ai/claude-agent-sdk@0.3.276" overrides: # Earlier React Navigation declarations create a circular type alias in TypeScript 7. diff --git a/scripts/build-desktop-artifact.ts b/scripts/build-desktop-artifact.ts index 4261c29d6a..a17850bbd4 100644 --- a/scripts/build-desktop-artifact.ts +++ b/scripts/build-desktop-artifact.ts @@ -3444,7 +3444,11 @@ export const packWindowsServerAsar = Effect.fn("packWindowsServerAsar")(function createPackageWithOptions(input.sourceDir, input.asarPath, { dot: true, unpack: WINDOWS_NATIVE_ASAR_UNPACK_GLOB, - globOptions: { ignore: resolveWindowsServerAsarIgnoreGlobs(input.arch) }, + // asar 4's glob matches ignore patterns relative to cwd. + globOptions: { + cwd: input.sourceDir, + ignore: resolveWindowsServerAsarIgnoreGlobs(input.arch), + }, }), catch: (cause) => new WindowsServerSidecarPackError({ asarPath: input.asarPath, cause }), }); diff --git a/scripts/package.json b/scripts/package.json index 0c4e289469..f16d1092bb 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@effect/platform-node": "catalog:", - "@electron/asar": "^3.4.1", + "@electron/asar": "^4.3.0", "@electron/osx-sign": "2.7.0", "@t3tools/shared": "workspace:*", "@t3tools/tailscale": "workspace:*", From 2dcb64e24261d63862f0df99a3c3a3986916fdd7 Mon Sep 17 00:00:00 2001 From: Trevor Walker Date: Fri, 25 Sep 2026 00:24:45 -0600 Subject: [PATCH 104/104] feat(chat): attach large pasted text without eager context (#844) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(chat): attach large pasted text without eager context * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record p… * feat(web): filter sidebar from thread menu (#821) * feat(web): add bounded route history shortcuts on desktop (#803) * feat(web): add bounded route history shortcuts * fix(web): provide browser shortcut context for history navigation * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(… * fix(web): add local dropped folders as path chips (#818) * feat(mobile): search projects when starting a task (#796) * feat(mobile): favorite models in thread settings (#790) * test(server): stabilize daemon and Git fixture timing (#855) * test(server): allow realistic socket cleanup before post-kill assertion * test(server): disable background Git GC in PR template fixture * fix(desktop): keep SnapShot shortcut helper out of Dock (#778) * fix(mobile): wait for deep-link thread hydration (#764) * fix(relay): stale Relay workers no longer flood a reopened thread (#852) * fix(relay): stop replaying stale Relay workers into reopened threads The startup receipt scan adopts every Relay MCP tool call ever recorded, so the first boot after the worker bridge shipped bound 284 long-finished jobs across 207 old turns. None could be observed, and each one published an idle "Relay observer unavailable" row into its original turn, dated now -- a wall of stale subagent cards on reopening the thread. An outage row now only explains a worker the thread already started, and an announced outage pushes its recheck back out instead of spawning the Relay CLI on every sweep for the life of the process. * fix(relay): clear the stale Relay outage rows already written The bridge no longer announces a worker it never started, but every projection that booted the earlier build still holds the idle rows it wrote. Drop the outage rows whose task has no start in the same thread, leaving the ones that explain a worker the thread really ran. * fix(relay): retire adopted jobs Relay reports as gone An outage that had already been announced returned before pushing its recheck back out, so every binding kept a Relay CLI spawn on each sweep. Adopted history made that 284 spawns a sweep on one projection. Push the recheck out for a worker that may still return, and retire a binding outright once Relay itself reports the job gone and nothing about it ever reached the thread. Absent Relay says nothing about the job, so that case keeps the binding and only slows the recheck. The four copies of the teardown are now one helper. * fix(relay): type the test engine helper against the SqlClient service * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(web): make provider accent color controls keyboard accessible (#805) * fix(web): show pull request state in context chips (#812) * fix(web): keep media controls clear and restore gallery arrows (#814) * fix(relay): retire a lost job for good instead of re-probing every boot Retirement only lived in memory, so every job the environment had ever run was adopted and re-probed on each start -- a cost that grows with history and never settles. Record the retirement on the binding row itself. The activity upserts on its id, so this writes no new row and stays out of the transcript, and boot skips the binding unless an activation was recorded after the watermark. A resume outranks the retirement and wakes the worker, then answers to the ordinary rules again. * fix(web): preserve browser tab shortcuts (#791) * fix(web): leave browser Mod-digit tabs to the browser * fix(server): distinguish persisted shortcut defaults from custom sets * fix(server): migrate only the complete legacy shortcut family * fix(mobile): keep screen awake during dictation (#799) * feat(web): allow manual disconnect of offline remote threads (#823) * fix(codex): explain a writer-locked conversation instead of the raw refusal (#854) Codex allows one writer per conversation across every app on the machine. When another Codex host holds that lock, `thread/resume` fails with `thread already has an active writer`, which Pylon passed straight through to the thread as a provider adapter error. Nothing told the user which app held the conversation or that closing it there was the fix. Detect that refusal at the resume site and replace it with a message naming the conversation and the action that releases it. The error stays a failure on purpose: falling back to `thread/start` here would strand the conversation's history behind a fresh one, so the recoverable-resume path must keep ignoring it. Adds a triage entry for the same symptom. * chore(mobile): check indexed access and overrides (#822) * fix(web): keep PR panel handoffs in the current thread (#826) * fix(web): keep PR panel handoffs in the current thread * Complete shortcut context fixture for web * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762… * fix(mobile): keep ordinary offline outbox retries out of warnings (#783) * fix(mobile): keep ordinary offline outbox retries out of warnings * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposi… * fix(web): respect panel motion and nested timeline scrolling (#750) Adapt T3 Code sources da6a85b1365993d0ff2a79698cd82498de247d8a, 7c2702d68ae2e10fa9d3a08c996ed47c393dcffd, and 68607c5a99147710b92228e0642f6c0a642e32a6 for Pylon composer and context controls. * fix(preview): prefer visible browser for new leases (#767) * fix(preview): preserve picking and align animated surfaces (#839) * fix(desktop): keep preview picking active across subframe navigation (#9741) Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit 1ab2dfb5a7bd2996f79407b5d02cae6132a7626c) * fix(web): keep browser pages aligned during panel animations (#12329) (cherry picked from commit 4cc984fedc33184c0a0fb67ea17fb02902ae2224) * fix(preview): preserve picking and align animated surfaces Keep element picking armed across subframe navigation, observe inline preview panel movement, and use the shared resize lifecycle for the sidebar rail. --------- Co-authored-by: Simone Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Julius Marminge * feat(mobile): rename threads from list menus (#786) * feat(mobile): rename threads from list menus * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider r… * fix(mobile): keep chat rows aligned after measurement (#777) * feat(web): add file context actions across diff and browser (#836) * feat(client): preview thread lifecycle actions until shell receipts (#819) * feat(web): choose themes from command palette (#837) * feat(web): choose themes from command palette * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider… * fix(mobile): recover from screen render failures (#768) * fix(marketing): update Astro image dependency chain (#774) * feat(web): show fenced optimistic pull request row actions (#838) * Sync linked PR changes and settle configured threads on events * feat(web): show fenced optimistic pull request row actions * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) * fix(usage): distinguish unavailable limits from empty limits (#781) * fix(mobile): send terminal delete from software keyboard (#770) * feat(mobile): copy thread IDs from list actions (#795) * fix(mobile): retain archive list during iOS search (#797) * fix(web): sort equally relevant thread search results by activity (#809) * feat(web): improve command and settings discovery * fix(web): sort equally relevant thread matches by activity * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * Start provider event worker before service exposure (#828) * fix(server): bind retained usage to verified accounts (#833) * fix: bind retained provider usage to verified accounts * fix(server): tag Codex reset identity failures * Sync linked PR changes and settle configured threads on events (#834) * fix(devices): identify legacy Android SDK command-line tools (#780) * feat(server): configure worktree submodule initialization (#800) * feat(server): configure worktree submodule initialization Adapt upstream 1262d2f3, 742173a1, and 0141bc2b for Pylon project settings and worktree entry points. * test(server): assert default submodule resolution in worktree recovery * perf(server): scope worktree fetch and parallel checkout (#820) * fix(desktop): bound WSL runtime cache retries and scratch cleanup (#741) * fix(desktop): bound WSL runtime cache retries and scratch cleanup * test(desktop): release WSL fixture lock with its holder * test(desktop): clean up WSL lock fixture on failure * test(desktop): trace WSL scratch lock shell regression * test(desktop): synchronize WSL prune lock fixture * fix(desktop): bound backend shutdown during quit (#763) * fix(desktop): bound backend shutdown wait during quit (#7599) Co-authored-by: Claude Fable 5.1 (cherry picked from commit 8ddd9f7efcdfb6e32b614595242b5986942d37b0) * fix(desktop): bound whole backend stop during quit --------- Co-authored-by: Ishaan Kothari Co-authored-by: Claude Fable 5.1 * docs(claude): clarify OpenRouter model selection (#816) * fix(web): label message copy action accurately (#779) * fix(web): keep PR checkout command during detail loading (#785) * fix(web): expose selected menu choices to assistive tech (#806) * fix(web): keep provider account emails readable (#810) * fix(web): expose switch state to screen readers (#755) * fix(mobile): show Move down icon on Android (#776) * fix(web): allow selecting text while renaming a thread (#807) * fix(server): preserve sibling checkpoint refs during rollback (#847) * fix(server): keep checkpoint failures out of public output (#851) * fix(server): reject stale Prime rollback responses after reconnect (#848) * feat(web): improve command and settings discovery (#801) * fix(web): keep settled threads closed when adding projects (#792) * fix(web): keep selection actions dismissed after button presses (#808) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari * fix(usage): refresh limits when the tab opens (#789) * fix(web): keep sidebar shelf rows moving together (#793) * fix(web): dismiss host menus on browser guest focus (#775) * fix(web): restore composer focus after option menus close (#782) * Use PR summary for hover previews (#831) * fix(web): match notification icons to thread events (#813) * fix(web): reveal composer context labels in tooltips (#829) * fix(agents): keep orphaned shell tasks out of the subagent cards (#853) A task's agent-vs-background identity was judged one row at a time. The next provider process settles a watcher it never launched and reports only the task id and status, so ingestion stamped that bare row "agent" even though the task had started as local_bash. The timeline then minted a subagent card per orphan, and the Agents panel — which still read the task as background — could never resolve it, so each card sat on "Ran 1 subagent · Status unavailable" forever. Resolve the classification across the thread instead: a task that names a background type anywhere keeps that identity, so a later bare row cannot promote it. Only a named background type makes the claim sticky, because Relay workers and native subagents legitimately carry no type at all and must keep deciding by their stamp. * fix(mobile): expose connection trace copy actions accessibly (#827) * feat(web): show timestamps on timeline work rows (#825) * fix(mobile): ignore bare wake links without navigation routes (#772) * fix(web): show pull request edit actions on touch (#802) * docs(upstream): correct deferred thought preview disposition (#773) * docs(upstream): correct deferred thought preview disposition * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh * fix(grok): show monitor and background shell lifecycle (#753) * fix(agents): suppress stale awareness alerts after restart (#758) * fix(server): stop replaying old agent alerts on restart (#13340) (cherry picked from commit e4eb9977f0b02b29cb2ee361b0c433c685e87615) * fix(relay): suppress stale agent awareness terminal alerts * fix(relay): accept live lifecycle events in startup millisecond --------- Co-authored-by: Julius Marminge * fix(usage): attribute shared homes by transcript source (#784) * fix(usage): attribute shared homes by transcript source * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 * fix(cursor): preserve internal agent errors (#754) * fix(ci): compare transfer ceilings only within a scenario (#759) * fix(usage): retain saved totals after transcript cleanup (#762) * fix(shared): keep the newest shared usage scan (#10315) Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> (cherry picked from commit d4d5d12e8ba086cfbf79ca3adeb4156b46ead665) * fix(usage): preserve saved totals after transcript cleanup (#12304) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> (cherry picked from commit 675869d2b0f9e059d5950b80390c0cad1cb1590e) * fix(usage): preserve Pylon native scan limits during retention * fix(usage): retain standalone Antigravity history after cleanup * fix(usage): prune retired source identities --------- Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> * fix(ssh): find Linuxbrew Node on WSL hosts (#769) * fix(server): generate PR diffs from branch changes (#13170) (#742) (cherry picked from commit f22331240ed1a97fae560e314bffb75fe39db8ca) Co-authored-by: Yash Singh * feat(observability): honor OpenTelemetry export kill switch (#746) * feat(observability): honor OpenTelemetry export kill switch * docs(upstream): record OTLP kill switch disposition * fix(server): release orphaned input after provider session stops (#739) * fix(server): release orphaned input after provider session stops * fix(server): repair cached orphaned request counts on upgrade * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refresh * docs(upstream): record provider refresh disposition * style(upstream): format provider refresh decision row * fix(git): prevent stale ref checkout from restoring files (#751) * fix(shared): preserve final quoted empty CSV records (#761) * fix(server): reduce background GitHub PR quota use (#749) * fix(server): background PR checks spend less GitHub quota (#13189) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 5975ec78b7eaff654e6183b99f8de7a05591e99d) * fix(server): background PR sync reads summaries in batches (#13198) Co-authored-by: Claude Opus 5.5 (cherry picked from commit 18de6bb328059703b5d1b0661610076e3c0fa2d4) * fix(server): GitHub PR lookups stop probing owner-qualified heads (#13200) Co-authored-by: Claude Opus 5.5 (cherry picked from commit eafb4a9340bd2c2271b7c8fac7eddd55beea3972) * fix(server): adapt quota tests to Pylon APIs * test(server): cover skipped terminal PR recheck * docs(upstream): link GitHub quota adoption * fix(server): avoid PR summary fallback on GraphQL quota failures * fix(server): preserve pending turn admission during provider bind (#752) A reused provider can still report its previous turn as running when a new exact turn is reserved. Bind that reservation as starting so matching turn.started can be accepted, reject malformed and terminal binds, and retain strict incarnation fences. * feat(chat): deliver buffered prose at natural boundaries (#757) * fix(server): show Claude subagent tool progress (#745) * fix(server): show Relay workers from installed CLI results (#744) * fix(relay): accept delegated thread IDs in activity routes (#756) * feat(prime): show child progress notes in Agents view (#748) * fix(server): bypass provider caches on explicit refresh (#743) * fix(server): bypass provider caches on explicit refres… * fix(devices): skip local SSH hosts and check selected environments (#766) * fix(server): skip device hosts that resolve to the local machine (#11698) (cherry picked from commit 1ced38a6647b7f466b535b4b413ca4a74303da8e) * fix(web): check SSH device hosts across selected environments Adapt upstream 8984f8103d0836c0b340fe70cd053428d622a8c3 to Pylon's selected-environment host ownership and retry UI. * fix(web): validate SSH host draft and fence scoped checks * fix(web): guard SSH host submit against blank labels * test(web): include scoped environments in integrations fixture --------- Co-authored-by: Julius Marminge --------- Co-authored-by: Julius Marminge Co-authored-by: Claude Opus 5.5 Co-authored-by: Aditya Garud <153842990+yashranaway@users.noreply.github.com> Co-authored-by: shivam <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: maria Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com> Co-authored-by: Yash Singh Co-authored-by: Ishaan Kothari Co-authored-by: Simone Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com> --- apps/desktop/src/ipc/DesktopIpcHandlers.ts | 2 + apps/desktop/src/ipc/channels.ts | 1 + apps/desktop/src/ipc/methods/window.test.ts | 54 +++ apps/desktop/src/ipc/methods/window.ts | 28 ++ apps/desktop/src/preload.ts | 1 + .../src/window/DesktopApplicationMenu.test.ts | 30 ++ .../src/window/DesktopApplicationMenu.ts | 36 +- .../src/environment/ServerEnvironment.ts | 1 + .../provider/Layers/ProviderService.test.ts | 80 +++++ .../src/provider/Layers/ProviderService.ts | 45 ++- .../acp/AntigravityAcpSupport.test.ts | 45 +++ .../src/provider/acp/AntigravityAcpSupport.ts | 15 + .../opencodeRuntime.cliParsers.test.ts | 14 + apps/server/src/provider/opencodeRuntime.ts | 7 + apps/web/src/components/ChatView.tsx | 84 ++++- .../components/ComposerPromptEditor.test.ts | 189 +++++----- .../src/components/ComposerPromptEditor.tsx | 7 +- apps/web/src/components/chat/ChatComposer.tsx | 333 ++++++++++++++++-- .../chat/composerAttachmentFiles.test.ts | 31 ++ .../chat/composerAttachmentFiles.ts | 13 +- .../components/composerInlineTokenPaste.ts | 61 ++-- .../files/AttachmentFilePreview.tsx | 21 ++ .../src/components/files/FilePreviewPanel.tsx | 22 +- apps/web/src/composerDraftStore.test.ts | 6 +- apps/web/src/composerDraftStore.ts | 5 + .../web/src/lib/attachmentUploadQueue.test.ts | 6 +- apps/web/src/lib/attachmentUploadQueue.ts | 2 +- .../src/lib/composerContextReferences.test.ts | 12 + apps/web/src/lib/composerContextReferences.ts | 27 +- apps/web/src/lib/desktopPasteAsText.test.ts | 32 ++ apps/web/src/lib/desktopPasteAsText.ts | 15 + apps/web/src/questionAttachments.test.ts | 77 +++- apps/web/src/questionAttachments.ts | 26 +- apps/web/src/routes/__root.tsx | 2 + packages/client-runtime/package.json | 4 + .../client-runtime/src/rpc/client.test.ts | 95 +++++ packages/client-runtime/src/rpc/client.ts | 31 ++ .../client-runtime/src/state/session.test.ts | 88 ++++- packages/client-runtime/src/state/session.ts | 77 +++- packages/client-runtime/src/textPaste.test.ts | 156 ++++++++ packages/client-runtime/src/textPaste.ts | 76 ++++ packages/contracts/src/environment.test.ts | 10 + packages/contracts/src/environment.ts | 3 + packages/contracts/src/ipc.ts | 2 + packages/contracts/src/orchestration.ts | 7 + .../shared/src/composerContextLegacy.test.ts | 16 + packages/shared/src/composerContextLegacy.ts | 19 +- .../src/composerContextLegacySend.test.ts | 24 ++ 48 files changed, 1744 insertions(+), 194 deletions(-) create mode 100644 apps/web/src/lib/desktopPasteAsText.test.ts create mode 100644 apps/web/src/lib/desktopPasteAsText.ts create mode 100644 packages/client-runtime/src/textPaste.test.ts create mode 100644 packages/client-runtime/src/textPaste.ts diff --git a/apps/desktop/src/ipc/DesktopIpcHandlers.ts b/apps/desktop/src/ipc/DesktopIpcHandlers.ts index b99d4219f7..579dbfac12 100644 --- a/apps/desktop/src/ipc/DesktopIpcHandlers.ts +++ b/apps/desktop/src/ipc/DesktopIpcHandlers.ts @@ -40,6 +40,7 @@ import { openExternal, openSystemSettings, checkSystemPermission, + pasteAsText, probeRemoteEditors, pickFolder, pickProjectFavicon, @@ -138,6 +139,7 @@ export const installDesktopIpcHandlers = Effect.fn("desktop.ipc.installHandlers" yield* ipc.handle(openExternal); yield* ipc.handle(openSystemSettings); yield* ipc.handle(checkSystemPermission); + yield* ipc.handle(pasteAsText); yield* ipc.handle(probeRemoteEditors); yield* ipc.handle(getUpdateState); yield* ipc.handle(downloadUpdate); diff --git a/apps/desktop/src/ipc/channels.ts b/apps/desktop/src/ipc/channels.ts index 99234d9f2a..2bc35d303c 100644 --- a/apps/desktop/src/ipc/channels.ts +++ b/apps/desktop/src/ipc/channels.ts @@ -8,6 +8,7 @@ export const OPEN_EXTERNAL_CHANNEL = "desktop:open-external"; export const OPEN_SYSTEM_SETTINGS_CHANNEL = "desktop:open-system-settings"; export const PROBE_REMOTE_EDITORS_CHANNEL = "desktop:probe-remote-editors"; export const MENU_ACTION_CHANNEL = "desktop:menu-action"; +export const PASTE_AS_TEXT_CHANNEL = "desktop:paste-as-text"; export const SNAP_SHOT_EVENT_CHANNEL = "desktop:snap-shot-event"; export const QUIT_SHORTCUT_CHANNEL = "desktop:quit-shortcut"; export const GET_WINDOW_FULLSCREEN_STATE_CHANNEL = "desktop:get-window-fullscreen-state"; diff --git a/apps/desktop/src/ipc/methods/window.test.ts b/apps/desktop/src/ipc/methods/window.test.ts index 1b21480a77..4d4ca7b63e 100644 --- a/apps/desktop/src/ipc/methods/window.test.ts +++ b/apps/desktop/src/ipc/methods/window.test.ts @@ -10,6 +10,15 @@ import { vi } from "vite-plus/test"; import type * as Electron from "electron"; +const { focusedWebContents, ownerWindow } = vi.hoisted(() => ({ + focusedWebContents: vi.fn(), + ownerWindow: vi.fn(), +})); +vi.mock("electron", () => ({ + webContents: { getFocusedWebContents: focusedWebContents }, + BrowserWindow: { fromWebContents: ownerWindow }, +})); + import * as DesktopBackendManager from "../../backend/DesktopBackendManager.ts"; import * as DesktopBackendPool from "../../backend/DesktopBackendPool.ts"; import * as ElectronDialog from "../../electron/ElectronDialog.ts"; @@ -17,6 +26,7 @@ import * as ElectronWindow from "../../electron/ElectronWindow.ts"; import { getLocalEnvironmentBootstraps, getWindowFullscreenState, + pasteAsText, pickProjectFavicon, probeRemoteEditors, } from "./window.ts"; @@ -158,6 +168,50 @@ describe("getWindowFullscreenState", () => { }); }); +describe("pasteAsText", () => { + it.effect( + "pastes into the focused guest only after the main renderer acknowledges the menu action", + () => { + const pasteAndMatchStyle = vi.fn(); + const mainPasteAndMatchStyle = vi.fn(); + const window = { + webContents: { id: 42, pasteAndMatchStyle: mainPasteAndMatchStyle }, + isDestroyed: () => false, + } as unknown as Electron.BrowserWindow; + focusedWebContents.mockReturnValue({ pasteAndMatchStyle, isDestroyed: () => false }); + ownerWindow.mockReturnValue(window); + + return Effect.gen(function* () { + yield* pasteAsText.handler(undefined, { sender: { id: 42 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + assert.equal(mainPasteAndMatchStyle.mock.calls.length, 0); + + yield* pasteAsText.handler(undefined, { sender: { id: 99 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + ownerWindow.mockReturnValue({}); // A focused PiP/other BrowserWindow. + yield* pasteAsText.handler(undefined, { sender: { id: 42 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + ownerWindow.mockReturnValue(null); // Detached contents. + yield* pasteAsText.handler(undefined, { sender: { id: 42 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + ownerWindow.mockReturnValue(window); + focusedWebContents.mockReturnValue({ pasteAndMatchStyle, isDestroyed: () => true }); + yield* pasteAsText.handler(undefined, { sender: { id: 42 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + focusedWebContents.mockReturnValue(null); + yield* pasteAsText.handler(undefined, { sender: { id: 42 } }); + assert.equal(pasteAndMatchStyle.mock.calls.length, 1); + }).pipe( + Effect.provide( + Layer.mock(ElectronWindow.ElectronWindow)({ + main: Effect.succeed(Option.some(window)), + }), + ), + ); + }, + ); +}); + describe("pickProjectFavicon", () => { it.effect("opens a single-image picker from the project directory", () => Effect.gen(function* () { diff --git a/apps/desktop/src/ipc/methods/window.ts b/apps/desktop/src/ipc/methods/window.ts index 1b8a9bbf89..06305551b9 100644 --- a/apps/desktop/src/ipc/methods/window.ts +++ b/apps/desktop/src/ipc/methods/window.ts @@ -339,6 +339,34 @@ export const probeRemoteEditors = DesktopIpc.makeIpcMethod({ }), }); +export const pasteAsText = DesktopIpc.makeIpcMethod({ + channel: IpcChannels.PASTE_AS_TEXT_CHANNEL, + payload: Schema.Undefined, + result: Schema.Void, + handler: Effect.fn("desktop.ipc.window.pasteAsText")(function* (_input, event) { + const electronWindow = yield* ElectronWindow.ElectronWindow; + const window = yield* electronWindow.main; + if ( + event === undefined || + Option.isNone(window) || + window.value.isDestroyed() || + window.value.webContents.id !== event.sender.id + ) { + return; + } + const focused = Electron.webContents.getFocusedWebContents(); + if ( + focused && + !focused.isDestroyed() && + Electron.BrowserWindow.fromWebContents(focused) === window.value + ) { + // Paste only the text representation into whichever app editor owns + // focus. The composer also intercepts the event to bypass folding. + focused.pasteAndMatchStyle(); + } + }), +}); + /** Theme files are a few KB; anything larger returns empty text and lets the * renderer reject it by size without the contents ever crossing the bridge. */ const PICKED_THEME_FILE_MAX_BYTES = 256 * 1024; diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index d9228cd04f..0eb191bf46 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -182,6 +182,7 @@ contextBridge.exposeInMainWorld("desktopBridge", { openSystemSettings: (pane: string) => ipcRenderer.invoke(IpcChannels.OPEN_SYSTEM_SETTINGS_CHANNEL, pane), probeRemoteEditors: () => ipcRenderer.invoke(IpcChannels.PROBE_REMOTE_EDITORS_CHANNEL, undefined), + pasteAsText: () => ipcRenderer.invoke(IpcChannels.PASTE_AS_TEXT_CHANNEL, undefined), onMenuAction: (listener) => { const wrappedListener = (_event: Electron.IpcRendererEvent, action: unknown) => { if (typeof action !== "string") return; diff --git a/apps/desktop/src/window/DesktopApplicationMenu.test.ts b/apps/desktop/src/window/DesktopApplicationMenu.test.ts index 2fdff27515..2e5279f721 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.test.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.test.ts @@ -152,6 +152,36 @@ describe("DesktopApplicationMenu", () => { }), ); + it.effect("owns Paste as Text and routes it through the renderer", () => + Effect.gen(function* () { + const selectedAction = yield* Deferred.make(); + const applicationMenuTemplate = + yield* Deferred.make(); + + yield* configureMenu(selectedAction, applicationMenuTemplate); + + const template = yield* Deferred.await(applicationMenuTemplate); + const editMenu = template.find((item) => item.label === "Edit"); + assert.isDefined(editMenu); + if (!Array.isArray(editMenu.submenu)) { + throw new Error("Expected Edit menu submenu to be an array."); + } + const pasteAsTextItem = editMenu.submenu.find((item) => item.label === "Paste as Text"); + assert.isDefined(pasteAsTextItem); + assert.equal(pasteAsTextItem.accelerator, "CmdOrCtrl+Shift+V"); + if (typeof pasteAsTextItem.click !== "function") { + throw new Error("Expected Paste as Text menu item to have a click handler."); + } + + pasteAsTextItem.click( + {} as Electron.MenuItem, + {} as Electron.BrowserWindow, + {} as KeyboardEvent, + ); + assert.equal(yield* Deferred.await(selectedAction), "paste-as-text"); + }), + ); + // Zoom must route through DesktopWindow.zoomMain instead of the Electron // zoom roles: the roles zoom whichever webContents has focus, which breaks // app zoom while an embedded preview WebContentsView holds focus. diff --git a/apps/desktop/src/window/DesktopApplicationMenu.ts b/apps/desktop/src/window/DesktopApplicationMenu.ts index 2d5c9bd5e9..eb67af5dc5 100644 --- a/apps/desktop/src/window/DesktopApplicationMenu.ts +++ b/apps/desktop/src/window/DesktopApplicationMenu.ts @@ -46,7 +46,9 @@ const dispatchMenuAction = Effect.fn("desktop.menu.dispatchMenuAction")(function action: string, ): Effect.fn.Return { const desktopWindow = yield* DesktopWindow.DesktopWindow; - yield* desktopWindow.dispatchMenuAction(action); + yield* desktopWindow.dispatchMenuAction(action, { + reveal: action !== "paste-as-text", + }); }); const zoomMainWindow = Effect.fn("desktop.menu.zoomMainWindow")(function* ( @@ -135,6 +137,9 @@ export const make = Effect.gen(function* () { const settingsClick = () => { runMenuEffect("open-settings", dispatchMenuAction("open-settings")); }; + const pasteAsTextClick = () => { + runMenuEffect("paste-as-text", dispatchMenuAction("paste-as-text")); + }; const zoomClick = (direction: DesktopWindow.MainWindowZoomDirection) => () => { runMenuEffect(`zoom-${direction}`, zoomMainWindow(direction)); }; @@ -184,7 +189,34 @@ export const make = Effect.gen(function* () { { role: environment.platform === "darwin" ? "close" : "quit" }, ], }, - { role: "editMenu" }, + { + label: "Edit", + submenu: [ + { role: "undo" }, + { role: "redo" }, + { type: "separator" }, + { role: "cut" }, + { role: "copy" }, + { role: "paste" }, + { + label: "Paste as Text", + accelerator: "CmdOrCtrl+Shift+V", + click: pasteAsTextClick, + }, + { role: "delete" }, + { type: "separator" }, + { role: "selectAll" }, + ...(environment.platform === "darwin" + ? [ + { type: "separator" as const }, + { + label: "Speech", + submenu: [{ role: "startSpeaking" as const }, { role: "stopSpeaking" as const }], + }, + ] + : []), + ], + }, { label: "View", submenu: [ diff --git a/apps/server/src/environment/ServerEnvironment.ts b/apps/server/src/environment/ServerEnvironment.ts index d63e44bcd9..c73f0ed17a 100644 --- a/apps/server/src/environment/ServerEnvironment.ts +++ b/apps/server/src/environment/ServerEnvironment.ts @@ -220,6 +220,7 @@ export const make = Effect.gen(function* () { attachmentUploads: true, questionAttachments: true, fileAttachments: { maxUploadBytes: PROVIDER_SEND_TURN_MAX_FILE_BYTES }, + pastedTextAttachments: true, pullRequests: true, inlineMessageContext: true, threadSettlement: true, diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index a961d5ab50..44027a0f29 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -3058,6 +3058,34 @@ routing.layer("ProviderServiceLive routing", (it) => { assert.include(followUpInput.input ?? "", '[Attached file "report.pdf" is saved at: '); assert.include(followUpInput.input ?? "", `${fileAttachment.id}.pdf]`); assert.deepEqual(followUpInput.attachments, [fileAttachment]); + const pastedTextAttachment = { + type: "file" as const, + id: "thread-attach-12345678-1234-1234-1234-123456789abc-txt", + name: "pasted-text.txt", + mimeType: "text/plain;charset=utf-8", + sizeBytes: 32_768, + source: { _tag: "pasted-text" as const }, + }; + routing.codex.sendTurn.mockClear(); + yield* provider.sendTurn({ + threadId: session.threadId, + input: "Investigate this crash", + attachments: [pastedTextAttachment], + }); + const pastedInput = routing.codex.sendTurn.mock.calls[0]?.[0] as ProviderSendTurnInput; + assert.include(pastedInput.input ?? "", '[Pasted text "pasted-text.txt" is saved at: '); + assert.include(pastedInput.input ?? "", ". Inspect it as needed.]"); + assert.deepEqual(pastedInput.attachments, [pastedTextAttachment]); + + routing.codex.followUp.mockClear(); + yield* provider.followUp({ + threadId: session.threadId, + input: "And inspect this paste", + attachments: [pastedTextAttachment], + }); + const pastedFollowUp = routing.codex.followUp.mock.calls[0]?.[0] as ProviderFollowUpInput; + assert.include(pastedFollowUp.input ?? "", '[Pasted text "pasted-text.txt" is saved at: '); + assert.deepEqual(pastedFollowUp.attachments, [pastedTextAttachment]); yield* provider.stopSession({ threadId: session.threadId }); }), @@ -6681,6 +6709,58 @@ turnAnalytics.layer("ProviderServiceLive turn analytics", (it) => { const validation = makeProviderServiceLayer(); validation.layer("ProviderServiceLive validation", (it) => { + it.effect("rejects input that leaves no room for pasted-text attachment context", () => + Effect.gen(function* () { + const provider = yield* ProviderService.ProviderService; + const attachment = { + type: "file" as const, + id: "thread-attach-12345678-1234-1234-1234-123456789abc-txt", + name: "pasted-text.txt", + mimeType: "text/plain;charset=utf-8", + sizeBytes: 32_768, + source: { _tag: "pasted-text" as const }, + }; + validation.codex.sendTurn.mockClear(); + + const failure = yield* provider + .sendTurn({ + threadId: asThreadId("thread-pasted-text-context-limit"), + input: "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS), + attachments: [attachment], + }) + .pipe(Effect.flip); + + assert.instanceOf(failure, ProviderValidationError); + assert.include(failure.issue, String(PROVIDER_SEND_TURN_MAX_INPUT_CHARS)); + assert.equal(validation.codex.sendTurn.mock.calls.length, 0); + }), + ); + + it.effect("rejects unsafe pasted-text attachment IDs before provider dispatch", () => + Effect.gen(function* () { + const provider = yield* ProviderService.ProviderService; + validation.codex.sendTurn.mockClear(); + const failure = yield* provider + .sendTurn({ + threadId: asThreadId("thread-pasted-text-invalid-path"), + input: "Inspect this paste", + attachments: [ + { + type: "file", + id: "../unsafe", + name: "pasted-text.txt", + mimeType: "text/plain;charset=utf-8", + sizeBytes: 32_768, + source: { _tag: "pasted-text" }, + }, + ], + }) + .pipe(Effect.flip); + assert.instanceOf(failure, ProviderValidationError); + assert.equal(validation.codex.sendTurn.mock.calls.length, 0); + }), + ); + it.effect("rejects citation-expanded input over the provider character limit", () => Effect.gen(function* () { const provider = yield* ProviderService.ProviderService; diff --git a/apps/server/src/provider/Layers/ProviderService.ts b/apps/server/src/provider/Layers/ProviderService.ts index e999423390..5494ff8356 100644 --- a/apps/server/src/provider/Layers/ProviderService.ts +++ b/apps/server/src/provider/Layers/ProviderService.ts @@ -528,30 +528,41 @@ const correlateRuntimeEventWithInstance = ( * makes the path line the sole channel a non-image attachment has on those * providers, which is why follow-ups need it exactly as much as turns do. * - * A context block that would push the input past the provider limit is left - * out. Unresolvable ids are skipped here and surface as adapter errors when the - * file is read. + * Ordinary context that would push input past the provider limit is left out. + * A pasted-text path is required: silently omitting it would leave the agent + * unable to inspect the folded paste. Unresolvable ids are skipped here and + * surface as adapter errors when the file is read. */ const appendAttachmentContext = ( attachmentsDir: string, input: string | undefined, attachments: ReadonlyArray, -): string | undefined => { +): { readonly input: string | undefined; readonly pastedTextOverflow: boolean } => { let inputWithContext = input; + let pastedTextOverflow = false; const append = (context: string | undefined) => { - if (context === undefined) return; + if (context === undefined) return true; const candidate = inputWithContext ? `${inputWithContext}\n\n${context}` : context; if (candidate.length <= PROVIDER_SEND_TURN_MAX_INPUT_CHARS) { inputWithContext = candidate; + return true; } + return false; }; for (const attachment of attachments) { const attachmentPath = resolveAttachmentPath({ attachmentsDir, attachment }); - append( + const isPastedText = + attachment.type === "file" && + "source" in attachment && + attachment.source?._tag === "pasted-text"; + const appended = append( attachmentPath === null ? undefined - : `[Attached ${attachment.type} "${attachment.name}" is saved at: ${attachmentPath}]`, + : isPastedText + ? `[Pasted text "${attachment.name}" is saved at: ${attachmentPath}. Inspect it as needed.]` + : `[Attached ${attachment.type} "${attachment.name}" is saved at: ${attachmentPath}]`, ); + if (isPastedText && (attachmentPath === null || !appended)) pastedTextOverflow = true; } for (const attachment of attachments) { const source = isChatImageAttachment(attachment) ? attachment.source : undefined; @@ -583,7 +594,7 @@ const appendAttachmentContext = ( : undefined, ); } - return inputWithContext; + return { input: inputWithContext, pastedTextOverflow }; }; const makeProviderService = Effect.fn("makeProviderService")(function* ( @@ -2326,11 +2337,18 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( // Pylon keeps the attachment context in a shared helper; feed it the // citation-expanded text so both transforms land in the provider prompt. - const inputTextWithAttachmentContext = appendAttachmentContext( + const attachmentContext = appendAttachmentContext( serverConfig.attachmentsDir, inputTextWithCitations, attachments, ); + if (attachmentContext.pastedTextOverflow) { + return yield* toValidationError( + "ProviderService.sendTurn", + `Pasted-text attachment path is unavailable or exceeds the ${PROVIDER_SEND_TURN_MAX_INPUT_CHARS} character input limit`, + ); + } + const inputTextWithAttachmentContext = attachmentContext.input; const input = { ...parsed, @@ -3358,11 +3376,18 @@ const makeProviderService = Effect.fn("makeProviderService")(function* ( // generic file attached to a follow-up is lost outright: every adapter // except OpenCode skips non-images, so the path line is the only thing // that tells the agent the file exists. - const followUpInputText = appendAttachmentContext( + const followUpContext = appendAttachmentContext( serverConfig.attachmentsDir, input.input, input.attachments, ); + if (followUpContext.pastedTextOverflow) { + return yield* toValidationError( + "ProviderService.followUp", + `Pasted-text attachment path is unavailable or exceeds the ${PROVIDER_SEND_TURN_MAX_INPUT_CHARS} character input limit`, + ); + } + const followUpInputText = followUpContext.input; return yield* followUpSession({ ...input, ...(followUpInputText !== undefined ? { input: followUpInputText } : {}), diff --git a/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts b/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts index 15ba88da3b..aee10782a6 100644 --- a/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts +++ b/apps/server/src/provider/acp/AntigravityAcpSupport.test.ts @@ -279,6 +279,51 @@ it.layer(NodeServices.layer)("buildAntigravityPrompt", (it) => { }), ); + it.effect("keeps folded clipboard text out of native context", () => + Effect.gen(function* () { + const fixture = yield* makeAttachmentFixture(); + const pastedText = { + ...textAttachment, + name: "pasted-text.txt", + mimeType: "text/plain", + source: { _tag: "pasted-text" as const }, + } satisfies ChatAttachment; + yield* fixture.write(pastedText, "A very large crash report"); + + const prompt = yield* buildAntigravityPrompt({ + input: "Inspect the pasted text only as needed.", + attachments: [pastedText], + attachmentsDir: fixture.attachmentsDir, + }); + + expect(prompt).toEqual([{ type: "text", text: "Inspect the pasted text only as needed." }]); + }), + ); + + it.effect("rejects missing folded clipboard text", () => + Effect.gen(function* () { + const fixture = yield* makeAttachmentFixture(); + const pastedText = { + ...textAttachment, + name: "pasted-text.txt", + mimeType: "text/plain", + source: { _tag: "pasted-text" as const }, + } satisfies ChatAttachment; + + const error = yield* buildAntigravityPrompt({ + input: "Inspect the pasted text only as needed.", + attachments: [pastedText], + attachmentsDir: fixture.attachmentsDir, + }).pipe(Effect.flip); + + expect(error).toMatchObject({ + _tag: "AcpRequestError", + code: -32602, + errorMessage: "Could not read attachment 'pasted-text.txt'.", + }); + }), + ); + it.effect("sends supported audio files as native audio content", () => Effect.gen(function* () { const fixture = yield* makeAttachmentFixture(); diff --git a/apps/server/src/provider/acp/AntigravityAcpSupport.ts b/apps/server/src/provider/acp/AntigravityAcpSupport.ts index 96bbee73a4..88be6e4b5b 100644 --- a/apps/server/src/provider/acp/AntigravityAcpSupport.ts +++ b/apps/server/src/provider/acp/AntigravityAcpSupport.ts @@ -289,6 +289,13 @@ export const buildAntigravityPrompt = Effect.fn("buildAntigravityPrompt")(functi let totalBytes = 0; for (const attachment of input.attachments ?? []) { + const isPastedText = + attachment.type === "file" && + "source" in attachment && + attachment.source?._tag === "pasted-text"; + // ProviderService has already put the file path in the text block. Keep a + // folded clipboard paste lazy so the agent can search or sample it rather + // than paying to embed the entire resource in context immediately. const mimeType = attachment.mimeType.toLowerCase().split(";", 1)[0] ?? ""; const image = attachment.type === "image" && IMAGE_MIME_TYPES.has(mimeType); const audio = attachment.type === "file" && AUDIO_MIME_TYPES.has(mimeType); @@ -321,6 +328,14 @@ export const buildAntigravityPrompt = Effect.fn("buildAntigravityPrompt")(functi ), ), ); + if (isPastedText) { + if (info.type !== "File") { + return yield* EffectAcpErrors.AcpRequestError.invalidParams( + `Could not read attachment '${attachment.name}'.`, + ); + } + continue; + } const size = Number(info.size); const limit = image ? PROVIDER_SEND_TURN_MAX_IMAGE_BYTES diff --git a/apps/server/src/provider/opencodeRuntime.cliParsers.test.ts b/apps/server/src/provider/opencodeRuntime.cliParsers.test.ts index 35a7791c62..c66a2f36a2 100644 --- a/apps/server/src/provider/opencodeRuntime.cliParsers.test.ts +++ b/apps/server/src/provider/opencodeRuntime.cliParsers.test.ts @@ -318,4 +318,18 @@ describe("toOpenCodeFileParts", () => { ["application/pdf", "text/markdown", "image/png"], ); }); + + it("keeps folded clipboard text on the lazy path fallback", () => { + const parts = toOpenCodeFileParts({ + attachments: [ + { + ...attachment("text/plain"), + source: { _tag: "pasted-text" as const }, + }, + ], + resolveAttachmentPath: () => "/tmp/pasted-text.txt", + }); + + NodeAssert.deepEqual(parts, []); + }); }); diff --git a/apps/server/src/provider/opencodeRuntime.ts b/apps/server/src/provider/opencodeRuntime.ts index ae23bccc10..4bb43cf2a2 100644 --- a/apps/server/src/provider/opencodeRuntime.ts +++ b/apps/server/src/provider/opencodeRuntime.ts @@ -491,6 +491,13 @@ export function toOpenCodeFileParts(input: { const parts: Array = []; for (const attachment of input.attachments ?? []) { + if ( + attachment.type === "file" && + "source" in attachment && + attachment.source?._tag === "pasted-text" + ) { + continue; + } if (!isOpenCodeNativeFilePart(attachment)) { continue; } diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 848a28752f..1735772920 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -61,6 +61,9 @@ import { isRollbackActive, type RollbackTarget, } from "@t3tools/client-runtime/rollback"; +import { shouldRoutePlainTextPaste } from "./composerInlineTokenPaste"; +import { isPasteAsTextShortcut } from "@t3tools/client-runtime/text-paste"; +import { connectedInitialConfigForState } from "@t3tools/client-runtime/state/session"; import { type CodexArtifactTemplate } from "@t3tools/client-runtime/codex-artifact-templates"; import { effectiveSnoozed, threadWokeAt } from "@t3tools/client-runtime/state/thread-settled"; import { @@ -112,6 +115,7 @@ import { flushSync } from "react-dom"; import { useLocation, useNavigate, useRouter } from "@tanstack/react-router"; import { assistantCitationsToPlainText } from "@t3tools/shared/assistantCitations"; import { assistantCitationFromLocation } from "../lib/assistantCitationNavigation"; +import { isMacPlatform } from "../lib/utils"; import type { AssistantCitationSourceAnchor } from "~/lib/assistantTextSelection"; import { useShallow } from "zustand/react/shallow"; import { getProviderAdmissionUnavailableReason } from "@t3tools/client-runtime/providerAvailability"; @@ -126,6 +130,7 @@ import * as Cause from "effect/Cause"; import * as Duration from "effect/Duration"; import * as Schema from "effect/Schema"; import { AsyncResult } from "effect/unstable/reactivity"; +import * as Option from "effect/Option"; import { isElectron } from "../env"; import { readLocalApi } from "../localApi"; import { useDiffPanelStore } from "../diffPanelStore"; @@ -551,7 +556,7 @@ import { clampFileAttachmentUploadBytes } from "@t3tools/client-runtime/state/at import { appAtomRegistry } from "../rpc/atomRegistry"; import { fileAttachmentCapabilityBlockReason } from "./chat/composerAttachmentFiles"; import { assetEnvironment } from "../state/assets"; -import { readPreparedConnection } from "../state/session"; +import { environmentSession, readPreparedConnection } from "../state/session"; import { useAtomCommand } from "../state/use-atom-command"; import { useAtomQueryRunner } from "../state/use-atom-query-runner"; import { Button } from "./ui/button"; @@ -768,6 +773,19 @@ function pasteTextToFocusComposer(event: ClipboardEvent): string | null { return text.length > 0 ? text : null; } +function supportsPastedTextAttachmentsNow(environmentId: EnvironmentId): boolean { + const connection = appAtomRegistry.get(environmentCatalog.stateAtom(environmentId)); + const leaseConfig = appAtomRegistry.get( + environmentSession.connectedInitialConfigAtom(environmentId), + ); + if (connection.waiting || leaseConfig.waiting) return false; + const config = connectedInitialConfigForState( + Option.getOrNull(AsyncResult.value(connection)), + Option.getOrElse(AsyncResult.value(leaseConfig), () => Option.none()), + ); + return config?.environment.capabilities.pastedTextAttachments === true; +} + function formatOutgoingPrompt(params: { provider: ProviderDriverKind; model: string | null; @@ -1764,6 +1782,7 @@ export default function ChatView(props: ChatViewProps) { const localComposerRef = useRef(null); const composerRef = useComposerHandleContext() ?? localComposerRef; const branchToolbarRef = useRef(null); + const pasteAsTextShortcutUntilRef = useRef(0); const [restingComposerControlsHost, setRestingComposerControlsHost] = useState(null); const [restingComposerControlsVisible, setRestingComposerControlsVisible] = useState(false); @@ -2759,10 +2778,30 @@ export default function ChatView(props: ChatViewProps) { observedPullRequestProviders, ]); const pullRequestsUnavailableGitHubUrl = pullRequestsUnavailableFallbackLink?.url ?? null; - const attachmentEnvironmentConfig = environmentById.get(environmentId)?.serverConfig ?? null; + const attachmentEnvironment = environmentById.get(environmentId) ?? null; + const attachmentEnvironmentConfig = attachmentEnvironment?.serverConfig ?? null; + // The presentation config may retain a previous server's capabilities. + // Both atoms carry the exact supervisor state object: if the environment ID + // reconnects to a different server, the old config cannot match its lease. + const attachmentConnectionResult = useAtomValue(environmentCatalog.stateAtom(environmentId)); + const attachmentLeaseConfigResult = useAtomValue( + environmentSession.connectedInitialConfigAtom(environmentId), + ); + const attachmentConnectionState = attachmentConnectionResult.waiting + ? null + : Option.getOrNull(AsyncResult.value(attachmentConnectionResult)); + const attachmentLeaseConfig = attachmentLeaseConfigResult.waiting + ? Option.none() + : Option.getOrElse(AsyncResult.value(attachmentLeaseConfigResult), () => Option.none()); + const attachmentBootstrapConfig = connectedInitialConfigForState( + attachmentConnectionState, + attachmentLeaseConfig, + ); const attachmentUploadsCapabilityKnown = attachmentEnvironmentConfig !== null; const supportsQuestionAttachments = attachmentEnvironmentConfig?.environment.capabilities.questionAttachments === true; + const supportsPastedTextAttachments = + attachmentBootstrapConfig?.environment.capabilities.pastedTextAttachments === true; const supportsAttachmentUploads = attachmentEnvironmentConfig?.environment.capabilities.attachmentUploads === true; const advertisedFileAttachmentBytes = @@ -7467,24 +7506,38 @@ export default function ChatView(props: ChatViewProps) { // so a paste that follows has no editable target and would be dropped. // Route it to the composer like a typed key, which also expands it. useEffect(() => { + const keyHandler = (event: KeyboardEvent) => { + if ( + shouldRedirectInputToComposer(event) && + isPasteAsTextShortcut(event, isMacPlatform(navigator.platform)) + ) { + pasteAsTextShortcutUntilRef.current = Date.now() + 1_000; + } + }; const handler = (event: ClipboardEvent) => { if (!activeThreadId || isCommandPaletteOpen()) return; if (getTerminalFocusOwner() !== null) return; if (composerRef.current?.isModelPickerOpen()) return; const text = pasteTextToFocusComposer(event); - if (text === null) return; + const clipboardData = event.clipboardData; + if (text === null || clipboardData === null) return; + const bypassAutoAttachment = Date.now() <= pasteAsTextShortcutUntilRef.current; + pasteAsTextShortcutUntilRef.current = 0; if ( - composerRef.current?.insertTextAtEnd( - text, - event.clipboardData ? { clipboardData: event.clipboardData } : undefined, - ) + (shouldRoutePlainTextPaste(clipboardData, bypassAutoAttachment) && + composerRef.current?.pasteTextAtEnd(text, { bypassAutoAttachment })) || + composerRef.current?.insertTextAtEnd(text, { clipboardData }) ) { event.preventDefault(); event.stopPropagation(); } }; + window.addEventListener("keydown", keyHandler, true); window.addEventListener("paste", handler, true); - return () => window.removeEventListener("paste", handler, true); + return () => { + window.removeEventListener("keydown", keyHandler, true); + window.removeEventListener("paste", handler, true); + }; }, [activeThreadId, composerRef]); const [pendingRevert, setPendingRevert] = useState<{ @@ -8157,6 +8210,7 @@ export default function ChatView(props: ChatViewProps) { supportsAttachmentUploads: liveSupportsAttachmentUploads, fileBlockReason: fileAttachmentCapabilityBlockReason({ files: composerFilesSnapshot, + supportsPastedTextAttachments: supportsPastedTextAttachmentsNow(environmentId), attachmentUploadsCapabilityKnown: config !== null, supportsAttachmentUploads: liveSupportsAttachmentUploads, maxFileAttachmentBytes: @@ -8283,6 +8337,7 @@ export default function ChatView(props: ChatViewProps) { mimeType: attachment.mimeType, sizeBytes: attachment.sizeBytes, downloadable: false, + ...(attachment.source ? { source: attachment.source } : {}), }, ); const shouldAnchorFirstMessage = @@ -9095,6 +9150,18 @@ export default function ChatView(props: ChatViewProps) { const draft = useComposerDraftStore.getState().getComposerDraft(target); const attachments = draft ? [...draft.images, ...draft.files] : []; if (attachments.length === 0) continue; + if ( + attachments.some( + (attachment) => attachment.type === "file" && attachment.source?._tag === "pasted-text", + ) && + !supportsPastedTextAttachmentsNow(environmentId) + ) { + setThreadError( + activeThreadId, + "This server cannot use a saved large-paste attachment. Remove it or reconnect to a server that supports large pastes.", + ); + return; + } const uploaded = getUploadedAttachments({ environmentId, images: attachments }); if (!uploaded) { setThreadError( @@ -10810,6 +10877,7 @@ export default function ChatView(props: ChatViewProps) { attachmentUploadsCapabilityKnown={attachmentUploadsCapabilityKnown} supportsAttachmentUploads={supportsAttachmentUploads} supportsQuestionAttachments={supportsQuestionAttachments} + supportsPastedTextAttachments={supportsPastedTextAttachments} maxFileAttachmentBytes={maxFileAttachmentBytes} routeKind={routeKind} routeThreadRef={routeThreadRef} diff --git a/apps/web/src/components/ComposerPromptEditor.test.ts b/apps/web/src/components/ComposerPromptEditor.test.ts index e295c72863..98bdfcc590 100644 --- a/apps/web/src/components/ComposerPromptEditor.test.ts +++ b/apps/web/src/components/ComposerPromptEditor.test.ts @@ -19,6 +19,8 @@ import { import { importPastedComposerText, + readPastedComposerContext, + shouldRoutePlainTextPaste, registerComposerInlineTokenPaste, } from "./composerInlineTokenPaste"; import { @@ -499,95 +501,108 @@ describe("registerComposerInlineTokenPaste", () => { }); describe("context reference paste", () => { - it.each(["focused", "blurred"])("imports structured paste when %s", (focus) => { - vi.stubGlobal("ClipboardEvent", TestClipboardEvent); - const editor = createEditor({ nodes: [ComposerCitationNode] }); - editor.update( - () => { - const paragraph = $createParagraphNode(); - $getRoot().append(paragraph); - paragraph.selectEnd(); - }, - { discrete: true }, - ); - const imported: string[] = []; - const importFragment = ( - fragment: import("@t3tools/contracts").ComposerContextClipboardFragment, - ) => { - imported.push(...fragment.records.map((record) => record.contextId)); - return new Map([ - ["img-old", "img-new"], - ["ctx-t", "ctx-t"], - ]); - }; - registerComposerInlineTokenPaste(editor, { - createMentionNode: (path) => $createTextNode(``), - createCitationNode: $createComposerCitationNode, - createContextReferenceNode: (reference) => - $createTextNode(``), - getExpandedAbsoluteOffsetForPoint: () => 0, - importContextFragment: importFragment, - }); - const event = new TestClipboardEvent( - "![shot](t3-context://v1/image/img-old) and [T](t3-context://v1/terminal/ctx-t)", - { - "web application/x-t3-context-fragment+json": JSON.stringify({ - version: 1, - source: { environmentId: "env-1" }, - records: [ - { - version: 1, - contextId: "img-old", - kind: "image", - label: "shot", - attachmentId: "a", - name: "shot.png", - mimeType: "image/png", - sizeBytes: 1, - }, - { - version: 1, - contextId: "ctx-t", - kind: "terminal", - label: "T", - terminalId: "t", - terminalLabel: "T", - lineStart: 1, - lineEnd: 1, - text: "x", - }, - { - version: 1, - contextId: "img-unrelated", - kind: "image", - label: "other", - attachmentId: "b", - name: "other.png", - mimeType: "image/png", - sizeBytes: 1, - }, - ], - }), - }, - ); - if (focus === "blurred") { - expect(importPastedComposerText(event.clipboardData, importFragment)).toBe( - "![shot](t3-context://v1/image/img-new) and [T](t3-context://v1/terminal/ctx-t)", + it.each([ + { focus: "focused", prefix: "" }, + { focus: "blurred", prefix: "" }, + { focus: "focused", prefix: "log ".repeat(10_000) }, + { focus: "blurred", prefix: "log ".repeat(10_000) }, + ])( + "imports structured paste when $focus with $prefix.length extra characters", + ({ focus, prefix }) => { + vi.stubGlobal("ClipboardEvent", TestClipboardEvent); + const editor = createEditor({ nodes: [ComposerCitationNode] }); + editor.update( + () => { + const paragraph = $createParagraphNode(); + $getRoot().append(paragraph); + paragraph.selectEnd(); + }, + { discrete: true }, + ); + const imported: string[] = []; + const importFragment = ( + fragment: import("@t3tools/contracts").ComposerContextClipboardFragment, + ) => { + imported.push(...fragment.records.map((record) => record.contextId)); + return new Map([ + ["img-old", "img-new"], + ["ctx-t", "ctx-t"], + ]); + }; + registerComposerInlineTokenPaste(editor, { + createMentionNode: (path) => $createTextNode(``), + createCitationNode: $createComposerCitationNode, + createContextReferenceNode: (reference) => + $createTextNode(``), + getExpandedAbsoluteOffsetForPoint: () => 0, + importContextFragment: importFragment, + }); + const event = new TestClipboardEvent( + `${prefix}![shot](t3-context://v1/image/img-old) and [T](t3-context://v1/terminal/ctx-t)`, + { + "web application/x-t3-context-fragment+json": JSON.stringify({ + version: 1, + source: { environmentId: "env-1" }, + records: [ + { + version: 1, + contextId: "img-old", + kind: "image", + label: "shot", + attachmentId: "a", + name: "shot.png", + mimeType: "image/png", + sizeBytes: 1, + }, + { + version: 1, + contextId: "ctx-t", + kind: "terminal", + label: "T", + terminalId: "t", + terminalLabel: "T", + lineStart: 1, + lineEnd: 1, + text: "x", + }, + { + version: 1, + contextId: "img-unrelated", + kind: "image", + label: "other", + attachmentId: "b", + name: "other.png", + mimeType: "image/png", + sizeBytes: 1, + }, + ], + }), + }, + ); + expect( + readPastedComposerContext(event.clipboardData)?.records.map((record) => record.contextId), + ).toEqual(["img-old", "ctx-t"]); + expect(shouldRoutePlainTextPaste(event.clipboardData, false)).toBe(false); + expect(shouldRoutePlainTextPaste(event.clipboardData, true)).toBe(true); + if (focus === "blurred") { + expect(importPastedComposerText(event.clipboardData, importFragment)).toBe( + `${prefix}![shot](t3-context://v1/image/img-new) and [T](t3-context://v1/terminal/ctx-t)`, + ); + expect(imported).toEqual(["img-old", "ctx-t"]); + return; + } + editor.update( + () => { + editor.dispatchCommand(PASTE_COMMAND, event as ClipboardEvent); + }, + { discrete: true }, ); expect(imported).toEqual(["img-old", "ctx-t"]); - return; - } - editor.update( - () => { - editor.dispatchCommand(PASTE_COMMAND, event as ClipboardEvent); - }, - { discrete: true }, - ); - expect(imported).toEqual(["img-old", "ctx-t"]); - expect(editor.getEditorState().read(() => $getRoot().getTextContent())).toBe( - " and ", - ); - }); + expect(editor.getEditorState().read(() => $getRoot().getTextContent())).toBe( + `${prefix} and `, + ); + }, + ); it("converts a copied legacy element into a sendable annotation and rewrites its link", () => { const copied = upgradeLegacyContextMessage( diff --git a/apps/web/src/components/ComposerPromptEditor.tsx b/apps/web/src/components/ComposerPromptEditor.tsx index 5bbcdec50c..a899823bf0 100644 --- a/apps/web/src/components/ComposerPromptEditor.tsx +++ b/apps/web/src/components/ComposerPromptEditor.tsx @@ -839,6 +839,7 @@ export interface ComposerPromptEditorHandle { focus: () => void; focusAt: (cursor: number) => void; focusAtEnd: () => void; + readSelectionRange: () => { start: number; end: number }; requestCitationComment: (request: ComposerCitationCommentRequest) => void; readSnapshot: () => { value: string; @@ -1861,6 +1862,10 @@ function ComposerPromptEditorInner({ ), ); }, + readSelectionRange: () => { + readSnapshot(); + return selectionRangeRef.current; + }, requestCitationComment: (request) => { citationCommentRequestRef.current = request; const target = editor @@ -2028,7 +2033,7 @@ function ComposerPromptEditorInner({ }} onKeyUp={(event) => onPageScrollKeyUp?.(event.key)} onBlur={onPageScrollRelease} - onPaste={onPaste} + onPasteCapture={onPaste} /> } placeholder={ diff --git a/apps/web/src/components/chat/ChatComposer.tsx b/apps/web/src/components/chat/ChatComposer.tsx index 990a9525cb..23870cc417 100644 --- a/apps/web/src/components/chat/ChatComposer.tsx +++ b/apps/web/src/components/chat/ChatComposer.tsx @@ -7,17 +7,20 @@ import { trackPendingContextImport } from "./pendingContextImport"; import { observeResponsiveBreakpointFade, usePanelAnimationSettings } from "../../panelAnimations"; import { usePrimaryEnvironmentId } from "../../state/environments"; import { readPrimaryEnvironmentTarget } from "../../environments/primary/target"; +import { DESKTOP_PASTE_AS_TEXT_EVENT } from "../../lib/desktopPasteAsText"; import { runtimeModeConfig, runtimeModeOptions } from "./runtimeModeConfig"; import { useRightPanelStore } from "~/rightPanelStore"; import { AttachmentFilePreview } from "../files/AttachmentFilePreview"; import { Dialog, DialogPopup, DialogTitle } from "../ui/dialog"; import { filterComposerPullRequestMatches } from "@t3tools/shared/composerPullRequestMatches"; -import { importPastedComposerText } from "../composerInlineTokenPaste"; +import { importPastedComposerText, readPastedComposerContext } from "../composerInlineTokenPaste"; import { elementContextToPreviewAnnotation } from "../../lib/elementContext"; import { RefreshIcon } from "~/components/ui/refresh-icon"; import { questionAttachmentDraftId, + countQuestionAttachments, + countCurrentDraftAttachments, useQuestionAttachmentPreparation, changeQuestionAttachmentPreparation, } from "../../questionAttachments"; @@ -52,6 +55,7 @@ import { PRIMARY_LOCAL_ENVIRONMENT_ID, PROVIDER_SEND_TURN_MAX_ATTACHMENTS, PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, + PROVIDER_SEND_TURN_MAX_INPUT_CHARS, } from "@t3tools/contracts"; import type { EnvironmentConnectionPresentation } from "@t3tools/client-runtime/connection"; import { resolveProviderContinuationTransition } from "@t3tools/client-runtime/providerContinuation"; @@ -90,6 +94,12 @@ import { sessionResourceViewIdentity, supportsSessionResourceReload, } from "@t3tools/client-runtime/state/session-resources"; +import { + isPasteAsTextShortcut, + nextPastedTextFileName, + pastedTextDisposition, + wouldTextPasteExceedLimit, +} from "@t3tools/client-runtime/text-paste"; import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger"; import { folderDropTarget, resolveDroppedFolderPath } from "./folderDrop"; import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model"; @@ -251,6 +261,7 @@ import { ensureInlineContextReferences, formatInlineContextReference, insertInlineContextReference, + inlineContextReferenceReplacement, toKindScopedComposerContextId, } from "~/lib/composerContextReferences"; import { @@ -347,7 +358,7 @@ import { } from "../../lib/snapShotAnimation"; import { resizeSnapShotSource } from "../../lib/snapShotSource"; import { basenameOfPath } from "../../pierre-icons"; -import { cn, randomUUID } from "~/lib/utils"; +import { cn, isMacPlatform, randomUUID } from "~/lib/utils"; import { getComposerPromptLengthValidationMessage, getComposerSubmissionValidationMessage, @@ -1398,6 +1409,8 @@ export interface ChatComposerHandle { text: string, options?: { ensureLeadingBoundary?: boolean; clipboardData?: DataTransfer }, ) => boolean; + /** Apply large-paste folding for text redirected from a blurred composer. */ + pasteTextAtEnd: (text: string, options?: { bypassAutoAttachment?: boolean }) => boolean; citeAssistantText: ( citation: AssistantCitation, sourceAnchor: AssistantCitationSourceAnchor, @@ -1460,6 +1473,7 @@ export interface ChatComposerProps { attachmentUploadsCapabilityKnown: boolean; supportsAttachmentUploads: boolean; supportsQuestionAttachments: boolean; + supportsPastedTextAttachments: boolean; maxFileAttachmentBytes: number | null; routeKind: "server" | "draft"; routeThreadRef: ScopedThreadRef; @@ -1653,6 +1667,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) attachmentUploadsCapabilityKnown, supportsAttachmentUploads, supportsQuestionAttachments, + supportsPastedTextAttachments, maxFileAttachmentBytes, routeKind, routeThreadRef, @@ -1907,6 +1922,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) }); const fileCapabilityBlockReason = fileAttachmentCapabilityBlockReason({ files: composerFiles, + supportsPastedTextAttachments, attachmentUploadsCapabilityKnown, supportsAttachmentUploads, maxFileAttachmentBytes, @@ -2974,6 +2990,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) // Refs // ------------------------------------------------------------------ const composerEditorRef = useRef(null); + const pasteAsTextShortcutUntilRef = useRef(0); + const pastedTextFileNamesRef = useRef<{ targetKey: string; names: Set }>({ + targetKey: "", + names: new Set(), + }); const attachmentInputRef = useRef(null); const composerFormRef = useRef(null); const composerSurfaceRef = useRef(null); @@ -3008,6 +3029,49 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) const isRevertingCheckpointRef = useRef(isRevertingCheckpoint); isRevertingCheckpointRef.current = isRevertingCheckpoint; + useEffect(() => { + const armPasteAsTextShortcut = () => { + // Electron can deliver its native menu action just before the paste + // event, while browsers normally deliver keydown first. A short deadline + // bridges both event paths without leaving later pastes in bypass mode. + pasteAsTextShortcutUntilRef.current = Date.now() + 1_000; + }; + const onKeyDown = (event: KeyboardEvent) => { + if ( + event.target instanceof Node && + composerFormRef.current?.contains(event.target) && + isPasteAsTextShortcut(event, isMacPlatform(navigator.platform)) + ) { + armPasteAsTextShortcut(); + } + }; + const onBlur = () => { + pasteAsTextShortcutUntilRef.current = 0; + }; + const onDesktopPasteAsText = () => { + const activeElement = document.activeElement; + const blocksPasteToFocus = + activeElement instanceof Element && + activeElement.closest( + 'input, textarea, select, button, a[href], summary, [contenteditable="true"], [contenteditable="plaintext-only"], [role="textbox"], [role="button"], [role="menuitem"], [role="option"]', + ) !== null; + if ( + (activeElement instanceof Node && composerFormRef.current?.contains(activeElement)) || + !blocksPasteToFocus + ) { + armPasteAsTextShortcut(); + } + }; + window.addEventListener(DESKTOP_PASTE_AS_TEXT_EVENT, onDesktopPasteAsText); + window.addEventListener("keydown", onKeyDown, true); + window.addEventListener("blur", onBlur); + return () => { + window.removeEventListener(DESKTOP_PASTE_AS_TEXT_EVENT, onDesktopPasteAsText); + window.removeEventListener("keydown", onKeyDown, true); + window.removeEventListener("blur", onBlur); + }; + }, []); + // ------------------------------------------------------------------ // Derived: composer send state // ------------------------------------------------------------------ @@ -5043,6 +5107,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) mimeType: file.mimeType, sizeBytes: file.sizeBytes, file: null, + ...(file.source ? { source: file.source } : {}), // An expired upload carries no ids, so it hydrates as a // needs-reattach row and the "Attach again" flow takes over. ...(expired @@ -5323,6 +5388,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) sizeBytes: file.sizeBytes, attachmentId: upload.attachmentId, environmentId, + ...(file.source ? { source: file.source } : {}), }); } // A repeat ⌘S on the *same* still-unencoded snapshot would stash it @@ -6203,11 +6269,38 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) // ------------------------------------------------------------------ // Callbacks: attachments // ------------------------------------------------------------------ + const countReservedAttachments = () => { + // Read the authoritative draft synchronously. React's file/image refs are + // refreshed by an effect, so two paste events in one render could both + // see the last free slot and the second folded paste would be swallowed. + const questionRequest = pendingUserInputs[0]; + const otherQuestionKeys = + questionAttachmentTarget && questionRequest && activeThreadId + ? questionRequest.questions + .map((question) => + questionAttachmentDraftId( + environmentId, + activeThreadId, + questionRequest.requestId, + question.id, + ), + ) + .filter((key) => key !== questionAttachmentTarget) + : []; + return ( + countCurrentDraftAttachments(attachmentDraftTarget, { + images: composerImagesRef.current.length, + files: composerFilesRef.current.length, + }) + + (pendingImageCompressionsRef.current.get(attachmentTargetKey) ?? 0) + + countQuestionAttachments(otherQuestionKeys) + ); + }; /** Resolves true when at least one chip was inserted for the accepted attachments. */ const addComposerAttachments = async ( files: File[], options?: { - readonly source?: string; + readonly source?: ChatFileAttachment["source"]; readonly selection?: { start: number; end: number }; readonly skipImageInlineChip?: boolean; }, @@ -6248,31 +6341,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) // accepted files reserve their attachment slots (via the pending counter) // before the first await, keeping the total under the limit. const pendingCount = pendingImageCompressionsRef.current.get(attachmentTargetKey) ?? 0; - // The attachment limit covers one whole set of answers, not each question. - const otherQuestionAttachments = - questionAttachmentTarget && pendingUserInputs[0] - ? pendingUserInputs[0].questions.reduce((count, question) => { - const target = questionAttachmentDraftId( - environmentId, - threadId, - pendingUserInputs[0]!.requestId, - question.id, - ); - if (target === questionAttachmentTarget) return count; - const draft = getComposerDraft(target); - return ( - count + - (draft?.images.length ?? 0) + - (draft?.files.length ?? 0) + - (useQuestionAttachmentPreparation.getState().counts[target] ?? 0) - ); - }, 0) - : 0; - let reservedCount = - composerImagesRef.current.length + - composerFilesRef.current.length + - pendingCount + - otherQuestionAttachments; + let reservedCount = countReservedAttachments(); // A pick that matches a needs-reattach marker replaces it in the draft, so // it must not consume a slot; a draft full of markers would otherwise hit // the capacity error before the replacement path could run. @@ -6343,6 +6412,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) mimeType: fileMimeType, sizeBytes: attachmentFile.size, file: attachmentFile, + ...(options?.source ? { source: options.source } : {}), }); } if (!matchingReattachMarker) { @@ -6356,8 +6426,30 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) // and a chip for it would point at nothing. const storedIds = new Set(addComposerFilesToDraft(acceptedFiles)); const storedFiles = acceptedFiles.filter((file) => storedIds.has(file.id)); + if (options?.source?._tag === "pasted-text" && storedFiles.length === 0) { + toastManager.add({ + type: "error", + title: "Pasted text could not be attached", + description: "Remove an attachment or try pasting again.", + data: { hideCopyButton: true }, + }); + } if (storedFiles.length > 0) { - insertedAny = insertAttachmentReferences(storedFiles.map(fileContextReference)); + insertedAny = insertAttachmentReferences( + storedFiles.map(fileContextReference), + options?.selection, + ); + } + if (options?.source?._tag === "pasted-text" && storedFiles.length > 0) { + const attached = storedFiles[0]!; + toastManager.add({ + type: "info", + title: `Large paste attached as ${attached.name}`, + description: `${formatAttachmentSize(attached.sizeBytes)} · Use ${ + isMacPlatform(navigator.platform) ? "⌘⇧V" : "Ctrl+Shift+V" + } to keep a large paste inline.`, + data: { hideCopyButton: true }, + }); } } if (acceptedImages.length === 0) return insertedAny; @@ -6445,11 +6537,16 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) */ const insertAttachmentReferences = ( references: ReadonlyArray, + selection?: { start: number; end: number }, ): boolean => { if (references.length === 0) return false; // Question answers carry attachments beside the answer, never as chips. Falling back to // the thread prompt here would hide the file behind a reference the question never shows. if (questionAttachmentTarget) return false; + if (selection) { + const edit = inlineContextReferenceReplacement(promptRef.current, selection, references); + return applyPromptReplacement(edit.start, edit.end, edit.text); + } const text = references.map(formatInlineContextReference).join(" "); const inserted = insertComposerText(`${text} `, "cursor", { ensureLeadingBoundary: true }); if (!inserted) { @@ -6483,24 +6580,151 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) // ------------------------------------------------------------------ // Callbacks: paste / drag // ------------------------------------------------------------------ + const foldPastedText = ( + plainText: string, + bypassAutoAttachment: boolean, + selectionOverride?: { start: number; end: number }, + ): boolean => { + const questionCanAttach = + pendingUserInputs.length === 0 || + (supportsQuestionAttachments && + activePendingProgress?.activeQuestion?.allowCustomAnswer !== false && + !activePendingIsResponding); + const hasAttachmentSlot = countReservedAttachments() < PROVIDER_SEND_TURN_MAX_ATTACHMENTS; + const selection = selectionOverride ?? composerEditorRef.current?.readSelectionRange(); + const wouldExceedInputLimit = wouldTextPasteExceedLimit({ + valueLength: promptRef.current.length, + selection: selection ?? { start: 0, end: 0 }, + textLength: plainText.length, + maxLength: PROVIDER_SEND_TURN_MAX_INPUT_CHARS, + }); + if (!supportsPastedTextAttachments) { + if (!wouldExceedInputLimit) return false; + toastManager.add({ + type: "error", + title: "Pasted text is too large for this message", + description: + "This server cannot attach large pasted text. Reduce the clipboard contents and try again.", + data: { hideCopyButton: true }, + }); + return true; + } + const shouldFold = + pastedTextDisposition({ + text: plainText, + bypassAutoAttachment, + wouldExceedInputLimit, + canAttach: true, + }) === "attachment"; + if (!shouldFold) { + return false; + } + + const canStageAttachment = + Boolean(activeThreadId) && + !isRevertingCheckpointRef.current && + questionCanAttach && + hasAttachmentSlot; + if (!canStageAttachment || fileStagingLimit === null) { + if (!wouldExceedInputLimit) { + return false; + } + toastManager.add({ + type: "error", + title: "Pasted text is too large for this message", + description: "Remove some text or an attachment, then paste again.", + data: { hideCopyButton: true }, + }); + return true; + } + + if (pastedTextFileNamesRef.current.targetKey !== attachmentTargetKey) { + pastedTextFileNamesRef.current = { targetKey: attachmentTargetKey, names: new Set() }; + } + const reservedNames = pastedTextFileNamesRef.current.names; + for (const file of composerFilesRef.current) reservedNames.add(file.name); + const foldedFileName = nextPastedTextFileName([...reservedNames]); + reservedNames.add(foldedFileName); + const foldedFile = new File([plainText], foldedFileName, { + type: "text/plain;charset=utf-8", + }); + if (foldedFile.size > fileStagingLimit) { + reservedNames.delete(foldedFileName); + if (!wouldExceedInputLimit) return false; + toastManager.add({ + type: "error", + title: "Pasted text is too large to attach", + description: "Reduce the clipboard contents or save a smaller excerpt as a file.", + data: { hideCopyButton: true }, + }); + return true; + } + + void addComposerAttachments([foldedFile], { + source: { _tag: "pasted-text" }, + ...(selection ? { selection } : {}), + }); + return true; + }; + const onComposerPaste = (event: React.ClipboardEvent) => { const files = Array.from(event.clipboardData.files); + const plainText = event.clipboardData.getData("text/plain"); + const bypassAutoAttachment = Date.now() <= pasteAsTextShortcutUntilRef.current; + pasteAsTextShortcutUntilRef.current = 0; + if (bypassAutoAttachment) { + // Paste as Text must consume the plain payload before Lexical's rich + // clipboard importer can hydrate context chips, HTML, or attached files. + event.preventDefault(); + event.stopPropagation(); + if (plainText.length > 0) { + const selection = composerEditorRef.current?.readSelectionRange() ?? { + start: promptRef.current.length, + end: promptRef.current.length, + }; + if ( + wouldTextPasteExceedLimit({ + valueLength: promptRef.current.length, + selection, + textLength: plainText.length, + maxLength: PROVIDER_SEND_TURN_MAX_INPUT_CHARS, + }) + ) { + toastManager.add({ + type: "error", + title: "Pasted text is too large for this message", + description: "Reduce the clipboard contents and try again.", + data: { hideCopyButton: true }, + }); + } else { + applyPromptReplacement(selection.start, selection.end, plainText); + } + } + return; + } // Claimable pastes go through even when agent questions are pending or the // composer is at its attachment limit: `addComposerAttachments` surfaces // those as a toast and a thread error. An early return here would swallow // the paste with no feedback. if ( - files.length === 0 || - !activeThreadId || - !shouldHandleComposerAttachmentPaste({ - files, - plainText: event.clipboardData.getData("text/plain"), - }) + files.length > 0 && + activeThreadId && + shouldHandleComposerAttachmentPaste({ files, plainText }) ) { + event.preventDefault(); + event.stopPropagation(); + void addComposerAttachments(files); return; } + + // Copied T3 chips need the structured importer to bring their records and files along. + if ((readPastedComposerContext(event.clipboardData)?.records.length ?? 0) > 0) return; + if (!foldPastedText(plainText, bypassAutoAttachment)) { + return; + } + event.preventDefault(); - void addComposerAttachments(files); + event.stopPropagation(); }; const insertComposerText = useCallback( @@ -6784,6 +7008,42 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) (pendingImageCompressionsRef.current.get(attachmentTargetKey) ?? 0) > 0 || pendingDraftWork.has(attachmentTargetKey), insertTextAtEnd: insertComposerTextAtEnd, + pasteTextAtEnd: (text: string, options) => { + const bypassAutoAttachment = + options?.bypassAutoAttachment === true || + Date.now() <= pasteAsTextShortcutUntilRef.current; + pasteAsTextShortcutUntilRef.current = 0; + const promptLength = promptRef.current.length; + if (bypassAutoAttachment) { + if ( + wouldTextPasteExceedLimit({ + valueLength: promptLength, + selection: { start: promptLength, end: promptLength }, + textLength: text.length, + maxLength: PROVIDER_SEND_TURN_MAX_INPUT_CHARS, + }) + ) { + toastManager.add({ + type: "error", + title: "Pasted text is too large for this message", + description: "Reduce the clipboard contents and try again.", + data: { hideCopyButton: true }, + }); + return true; + } + return insertComposerTextAtEnd(text); + } + if ( + !foldPastedText(text, bypassAutoAttachment, { + start: promptLength, + end: promptLength, + }) + ) { + return false; + } + focusComposer(); + return true; + }, citeAssistantText: (citation, sourceAnchor) => insertComposerText( formatAssistantCitationForComposer(citation, citation.comment), @@ -6923,6 +7183,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps) [ activeThread, addComposerAttachments, + foldPastedText, composerDraftTarget, composerCursor, composerTerminalContexts, diff --git a/apps/web/src/components/chat/composerAttachmentFiles.test.ts b/apps/web/src/components/chat/composerAttachmentFiles.test.ts index 193062e024..50f35805be 100644 --- a/apps/web/src/components/chat/composerAttachmentFiles.test.ts +++ b/apps/web/src/components/chat/composerAttachmentFiles.test.ts @@ -195,6 +195,37 @@ describe("composer attachment files", () => { ).toBe(unsupportedReason); }); + it("retains but blocks a marked paste after a server downgrade", () => { + const files = [ + { + name: "pasted-text.txt", + sizeBytes: 32_768, + source: { _tag: "pasted-text" }, + }, + ]; + const baseline = { + files, + attachmentUploadsCapabilityKnown: true, + supportsAttachmentUploads: true, + maxFileAttachmentBytes: 50 * 1024 * 1024, + }; + expect( + fileAttachmentCapabilityBlockReason({ + ...baseline, + supportsPastedTextAttachments: true, + }), + ).toBeNull(); + expect(fileAttachmentCapabilityBlockReason(baseline)).toContain( + "cannot use a saved large-paste attachment", + ); + expect( + fileAttachmentCapabilityBlockReason({ + ...baseline, + files: [{ name: "report.txt", sizeBytes: 32_768 }], + }), + ).toBeNull(); + }); + it("blocks retained files that exceed a newly lower server limit", () => { expect( fileAttachmentCapabilityBlockReason({ diff --git a/apps/web/src/components/chat/composerAttachmentFiles.ts b/apps/web/src/components/chat/composerAttachmentFiles.ts index 3166eeff41..c326f770a1 100644 --- a/apps/web/src/components/chat/composerAttachmentFiles.ts +++ b/apps/web/src/components/chat/composerAttachmentFiles.ts @@ -18,6 +18,7 @@ interface FileAttachmentCapabilityState { readonly attachmentUploadsCapabilityKnown: boolean; readonly supportsAttachmentUploads: boolean; readonly maxFileAttachmentBytes: number | null; + readonly supportsPastedTextAttachments?: boolean; } const IMAGE_MIME_TYPE_BY_EXTENSION: Readonly> = { @@ -113,12 +114,22 @@ export function fileAttachmentStagingLimit(input: FileAttachmentCapabilityState) /** Why retained generic files cannot send with the current server config. */ export function fileAttachmentCapabilityBlockReason( input: FileAttachmentCapabilityState & { - readonly files: ReadonlyArray<{ readonly name: string; readonly sizeBytes: number }>; + readonly files: ReadonlyArray<{ + readonly name: string; + readonly sizeBytes: number; + readonly source?: { readonly _tag: string } | undefined; + }>; }, ): string | null { if (input.files.length === 0) { return null; } + if ( + input.supportsPastedTextAttachments !== true && + input.files.some((file) => file.source?._tag === "pasted-text") + ) { + return "This server cannot use a saved large-paste attachment. Remove it or reconnect to a server that supports large pastes."; + } if (!input.attachmentUploadsCapabilityKnown) { return "Waiting for the server before file attachments can send"; } diff --git a/apps/web/src/components/composerInlineTokenPaste.ts b/apps/web/src/components/composerInlineTokenPaste.ts index 8db94a4207..f85589dcd8 100644 --- a/apps/web/src/components/composerInlineTokenPaste.ts +++ b/apps/web/src/components/composerInlineTokenPaste.ts @@ -146,40 +146,51 @@ export function registerComposerInlineTokenPaste( ); } -/** Imports the same structured clipboard payload for focused paste and paste-to-focus. */ -export function importPastedComposerText( +/** Clipboard records referenced by the copied text, including dependent screenshots. */ +export function readPastedComposerContext( clipboardData: Pick, - importContextFragment?: ComposerInlineTokenPasteOptions["importContextFragment"], -): string { +): ComposerContextClipboardFragment | null { const pastedText = clipboardData.getData("text/plain"); // Only records whose links are in the pasted text get imported; a fragment may carry // more (it was built for a larger copy) and must not start transfers for those. - const decodedFragment = importContextFragment - ? (decodeComposerContextFragment(clipboardData.getData(COMPOSER_CONTEXT_CLIPBOARD_MIME)) ?? - decodeComposerContextClipboardHtml(clipboardData.getData("text/html"))) - : null; + const decodedFragment = + decodeComposerContextFragment(clipboardData.getData(COMPOSER_CONTEXT_CLIPBOARD_MIME)) ?? + decodeComposerContextClipboardHtml(clipboardData.getData("text/html")); + if (decodedFragment === null) return null; const pastedIds = new Set( collectComposerContextReferences(pastedText).map((occurrence) => occurrence.contextId), ); - if (decodedFragment) { - for (const record of decodedFragment.records) { - if ( - record.kind === "preview-annotation" && - !("payload" in record) && - pastedIds.has(record.contextId) && - record.screenshotContextId - ) { - pastedIds.add(record.screenshotContextId); - } + for (const record of decodedFragment.records) { + if ( + record.kind === "preview-annotation" && + !("payload" in record) && + pastedIds.has(record.contextId) && + record.screenshotContextId + ) { + pastedIds.add(record.screenshotContextId); } } - const fragment = - decodedFragment === null - ? null - : { - ...decodedFragment, - records: decodedFragment.records.filter((record) => pastedIds.has(record.contextId)), - }; + return { + ...decodedFragment, + records: decodedFragment.records.filter((record) => pastedIds.has(record.contextId)), + }; +} + +/** Paste as Text wins over a copied rich fragment when the composer is blurred. */ +export function shouldRoutePlainTextPaste( + clipboardData: Pick, + pasteAsText: boolean, +): boolean { + return pasteAsText || (readPastedComposerContext(clipboardData)?.records.length ?? 0) === 0; +} + +/** Imports the same structured clipboard payload for focused paste and paste-to-focus. */ +export function importPastedComposerText( + clipboardData: Pick, + importContextFragment?: ComposerInlineTokenPasteOptions["importContextFragment"], +): string { + const pastedText = clipboardData.getData("text/plain"); + const fragment = importContextFragment ? readPastedComposerContext(clipboardData) : null; const rewrittenIds = fragment && fragment.records.length > 0 ? importContextFragment!(fragment) : null; const recordsById = new Map(fragment?.records.map((record) => [record.contextId, record]) ?? []); diff --git a/apps/web/src/components/files/AttachmentFilePreview.tsx b/apps/web/src/components/files/AttachmentFilePreview.tsx index 7de994a2f9..c6d705deca 100644 --- a/apps/web/src/components/files/AttachmentFilePreview.tsx +++ b/apps/web/src/components/files/AttachmentFilePreview.tsx @@ -12,6 +12,7 @@ import { Eye, Table2, Trash2Icon, + WrapTextIcon, XIcon, } from "lucide-react"; import { lazy, Suspense, useEffect, useMemo, useRef, useState } from "react"; @@ -21,6 +22,7 @@ import ChatMarkdown from "~/components/ChatMarkdown"; import { ScrollArea } from "~/components/ui/scroll-area"; import { toastManager } from "~/components/ui/toast"; import { useCopyToClipboard } from "~/hooks/useCopyToClipboard"; +import { useClientSettings, useUpdateClientSettings } from "~/hooks/useSettings"; import { cn } from "~/lib/utils"; import { AudioPreview } from "./AudioPreview"; @@ -193,6 +195,16 @@ export function AttachmentFilePreview(props: { return () => controller.abort(); }, [url, needsText, revision, props.sizeBytes, props.file, refresh]); const failure = error ?? (needsText ? contentError : null); + const wordWrap = useClientSettings((settings) => settings.wordWrap); + const updateClientSettings = useUpdateClientSettings(); + // Only the raw-text body honours word wrap. A rendered table or Markdown lays itself out, + // so offering the toggle there would be a control that visibly does nothing. + const showsRawText = + failure === null && + needsText && + content !== null && + !(delimiter && rendered) && + !(kind === "markdown" && rendered); const save = () => { if (saveController.current) return; @@ -325,6 +337,15 @@ export function AttachmentFilePreview(props: { )} ) : null} + {showsRawText ? ( + updateClientSettings({ wordWrap: !wordWrap })} + > + + + ) : null} {content ? (