refactor: compact recovery ownership and model metadata (#126751)

* refactor: compact recovery ownership and model metadata

* chore: respect release-owned changelog gate

* test: skip unsupported Windows permission assertions
This commit is contained in:
Peter Steinberger
2026-08-20 10:30:21 -07:00
committed by GitHub
parent aafa61be65
commit 3c5cecee06
11 changed files with 71 additions and 247 deletions
-162
View File
@@ -86,16 +86,9 @@
"models": [
{
"id": "kimi-k2.5",
"name": "kimi-k2.5",
"status": "deprecated",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -105,15 +98,8 @@
},
{
"id": "kimi-k2.6",
"name": "kimi-k2.6",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -123,15 +109,8 @@
},
{
"id": "kimi-k2.7-code",
"name": "kimi-k2.7-code",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -141,7 +120,6 @@
},
{
"id": "kimi-k3",
"name": "kimi-k3",
"reasoning": true,
"input": ["text", "image"],
"cost": {
@@ -159,15 +137,8 @@
},
{
"id": "deepseek-v4-flash",
"name": "deepseek-v4-flash",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
@@ -178,15 +149,8 @@
},
{
"id": "deepseek-v4-flash:0731",
"name": "deepseek-v4-flash:0731",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
@@ -196,15 +160,8 @@
},
{
"id": "deepseek-v4-flash:preview",
"name": "deepseek-v4-flash:preview",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
@@ -214,15 +171,8 @@
},
{
"id": "deepseek-v4-pro",
"name": "deepseek-v4-pro",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
@@ -233,15 +183,8 @@
},
{
"id": "deepseek-v4-pro:0813",
"name": "deepseek-v4-pro:0813",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
@@ -251,15 +194,8 @@
},
{
"id": "deepseek-v4-pro:preview",
"name": "deepseek-v4-pro:preview",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 524288,
"maxTokens": 8192,
"compat": {
@@ -269,15 +205,8 @@
},
{
"id": "gemma4",
"name": "gemma4",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -287,15 +216,8 @@
},
{
"id": "gemma4:31b",
"name": "gemma4:31b",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -305,15 +227,8 @@
},
{
"id": "glm-5.1",
"name": "glm-5.1",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 202752,
"maxTokens": 8192,
"compat": {
@@ -324,15 +239,8 @@
},
{
"id": "glm-5.2",
"name": "glm-5.2",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1000000,
"maxTokens": 8192,
"compat": {
@@ -343,15 +251,8 @@
},
{
"id": "gpt-oss:120b",
"name": "gpt-oss:120b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 131072,
"maxTokens": 8192,
"compat": {
@@ -361,15 +262,8 @@
},
{
"id": "gpt-oss:20b",
"name": "gpt-oss:20b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 131072,
"maxTokens": 8192,
"compat": {
@@ -379,15 +273,8 @@
},
{
"id": "minimax-m2.7",
"name": "minimax-m2.7",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 196608,
"maxTokens": 8192,
"compat": {
@@ -397,15 +284,8 @@
},
{
"id": "minimax-m3",
"name": "minimax-m3",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 524288,
"maxTokens": 8192,
"compat": {
@@ -415,15 +295,8 @@
},
{
"id": "mistral-large-3:675b",
"name": "mistral-large-3:675b",
"reasoning": false,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -433,15 +306,8 @@
},
{
"id": "nemotron-3-nano:30b",
"name": "nemotron-3-nano:30b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -451,15 +317,8 @@
},
{
"id": "nemotron-3-super",
"name": "nemotron-3-super",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -469,15 +328,8 @@
},
{
"id": "nemotron-3-ultra",
"name": "nemotron-3-ultra",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -487,15 +339,8 @@
},
{
"id": "qwen3.5",
"name": "qwen3.5",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -505,15 +350,8 @@
},
{
"id": "qwen3.5:397b",
"name": "qwen3.5:397b",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
@@ -11,6 +11,7 @@ import {
getAgentEventLifecycleGeneration,
rotateAgentEventLifecycleGeneration,
} from "../../infra/agent-events.js";
import * as recoveryOwnerRelease from "./main-session-recovery-owner-release.js";
import {
claimMainSessionRecoveryOwner,
commitMainSessionRecovery,
@@ -403,10 +404,10 @@ describe("main session recovery store", () => {
},
);
const onDeferredSuccess = vi.fn();
const immediateRelease = releaseMainSessionRecoveryOwner(claim.lease, {
onDeferredSuccess,
});
const schedulePending = vi
.spyOn(recoveryOwnerRelease, "scheduleMainSessionRecoveryPendingTarget")
.mockImplementation(() => {});
const immediateRelease = releaseMainSessionRecoveryOwner(claim.lease);
const immediateReleaseRejected = expect(immediateRelease).rejects.toThrow(
"transient session-store failure",
);
@@ -418,11 +419,13 @@ describe("main session recovery store", () => {
await vi.waitFor(() => {
expect(read().mainRestartRecovery?.foregroundClaims).toBeUndefined();
});
expect(onDeferredSuccess).toHaveBeenCalledWith({
sessionId: "session-1",
sessionKey,
storePath,
});
await vi.waitFor(() =>
expect(schedulePending).toHaveBeenCalledWith({
sessionId: "session-1",
sessionKey,
storePath,
}),
);
} finally {
vi.useRealTimers();
}
@@ -340,35 +340,8 @@ async function releaseMainSessionRecoveryOwnerWithRetries(
return { sessionId: entry.sessionId, sessionKey, storePath: lease.storePath };
}
function scheduleMainSessionRecoveryOwnerRelease(
lease: MainSessionRecoveryOwnerLease,
onDeferredSuccess?: (
pending: MainSessionRecoveryPendingTarget | undefined,
) => void | Promise<void>,
): void {
// A token is process-owned but durably blocks recovery. Keep exact-token
// cleanup alive through transient writer outages until release or restart.
scheduleMainSessionRecoveryMutation({
mutation: () => releaseMainSessionRecoveryOwnerWithRetries(lease),
onSuccess:
onDeferredSuccess ??
(async (pending) => {
if (pending) {
const { scheduleMainSessionRecoveryPendingTarget } =
await import("./main-session-recovery-owner-release.js");
scheduleMainSessionRecoveryPendingTarget(pending);
}
}),
});
}
export async function releaseMainSessionRecoveryOwner(
lease: MainSessionRecoveryOwnerLease | undefined,
options?: {
onDeferredSuccess?: (
pending: MainSessionRecoveryPendingTarget | undefined,
) => void | Promise<void>;
},
): Promise<MainSessionRecoveryPendingTarget | undefined> {
if (!lease) {
return undefined;
@@ -376,7 +349,17 @@ export async function releaseMainSessionRecoveryOwner(
try {
return await releaseMainSessionRecoveryOwnerWithRetries(lease);
} catch (error) {
scheduleMainSessionRecoveryOwnerRelease(lease, options?.onDeferredSuccess);
// Exact-token cleanup survives transient writer outages without blocking its caller.
scheduleMainSessionRecoveryMutation({
mutation: () => releaseMainSessionRecoveryOwnerWithRetries(lease),
onSuccess: async (pending) => {
if (pending) {
const { scheduleMainSessionRecoveryPendingTarget } =
await import("./main-session-recovery-owner-release.js");
scheduleMainSessionRecoveryPendingTarget(pending);
}
},
});
throw error;
}
}
@@ -1,6 +1,8 @@
// Lifecycle retry-grace e2e tests cover completion delivery retry behavior when
// lifecycle events race gateway waits or transient announce failures.
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { SessionDeliveryState } from "../../../config/sessions/types.js";
import type { AgentRunTerminalReplySnapshot } from "../../agent-run-terminal-reply.js";
import { testing as subagentAnnounceDeliveryTesting } from "../announce/subagent-announce-delivery.test-support.js";
import { testing as subagentAnnounceOutputTesting } from "../announce/subagent-announce-output.test-support.js";
import { testing as subagentAnnounceTesting } from "../announce/subagent-announce.js";
@@ -17,6 +19,7 @@ type LifecycleData = {
endedAt?: number;
aborted?: boolean;
error?: string;
terminalReply?: AgentRunTerminalReplySnapshot;
};
type LifecycleEvent = {
stream?: string;
@@ -28,10 +31,7 @@ type LifecycleEvent = {
type SessionStoreEntry = {
sessionId: string;
updatedAt: number;
channel?: string;
lastChannel?: string;
to?: string;
accountId?: string;
delivery?: SessionDeliveryState;
};
type GatewayAgentInternalEvent = {
@@ -149,10 +149,12 @@ describe("subagent registry lifecycle error grace", () => {
"agent:main:main": {
sessionId: "sess-main",
updatedAt: 1,
channel: "discord",
lastChannel: "discord",
to: "user-1",
accountId: "default",
delivery: {
kind: "external",
route: { channel: "discord", accountId: "default", target: { to: "user-1" } },
context: { channel: "discord", to: "user-1", accountId: "default" },
origin: { provider: "discord", to: "user-1", accountId: "default" },
},
},
},
{
@@ -394,7 +396,11 @@ describe("subagent registry lifecycle error grace", () => {
registerCompletionRun(runId, "completed-before-yield", "finish once", requesterTurnRunId);
setAssistantOutput(childSessionKey, "child complete");
emitLifecycleEvent(runId, { phase: "end", endedAt: Date.now() });
emitLifecycleEvent(runId, {
phase: "end",
endedAt: Date.now(),
terminalReply: { disposition: "visible", text: "child complete" },
});
await waitForDeliveredCleanup(runId);
const completed = mod
@@ -47,10 +47,9 @@ vi.mock(
...actual,
releaseMainSessionRecoveryOwner: async (
lease: Parameters<typeof actual.releaseMainSessionRecoveryOwner>[0],
options: Parameters<typeof actual.releaseMainSessionRecoveryOwner>[1],
) => {
await recoveryOwnerReleaseMocks.beforeRelease();
return await actual.releaseMainSessionRecoveryOwner(lease, options);
return await actual.releaseMainSessionRecoveryOwner(lease);
},
};
},
+1 -3
View File
@@ -63,9 +63,7 @@ async function releaseReplyRecoveryOwner(
return undefined;
}
try {
return await releaseMainSessionRecoveryOwner(lease, {
onDeferredSuccess: scheduleMainSessionRecoveryPendingTarget,
});
return await releaseMainSessionRecoveryOwner(lease);
} catch (error) {
log.warn(`failed to release main-session recovery reply owner: ${formatErrorMessage(error)}`);
// The durable owner schedules exact-token retries. A completed reply must
@@ -166,15 +166,12 @@ async function applySqliteSessionEntryReplacementProjection<T, TReplacement>(
(transactionDb) => {
const transactionEntries = new Map<string, SessionEntry>();
for (const sessionKey of validationKeys) {
const transactionEntry = readExactSessionEntryRow(transactionDb, sessionKey)?.entry;
if (
readExactSessionEntryJson(transactionDb, sessionKey) !==
expectedEntryJson.get(sessionKey)
) {
const transactionRow = readExactSessionEntryRow(transactionDb, sessionKey);
if (transactionRow?.row.entry_json !== expectedEntryJson.get(sessionKey)) {
throw new Error(`SQLite session entry changed before replacement for ${sessionKey}`);
}
if (transactionEntry) {
transactionEntries.set(sessionKey, transactionEntry);
if (transactionRow) {
transactionEntries.set(sessionKey, transactionRow.entry);
}
}
for (const replacement of applicable) {
+1 -2
View File
@@ -340,7 +340,7 @@ export async function runCronIsolatedAgentTurn(params: {
} finally {
try {
if (!cronRunSessionCleanupAttempted) {
const cleanupOutcome = await cleanupCronRunSessionAfterRun({
await cleanupCronRunSessionAfterRun({
job: params.job,
agentSessionKey: prepared.context.agentSessionKey,
sessionId: prepared.context.currentRunSessionId(),
@@ -349,7 +349,6 @@ export async function runCronIsolatedAgentTurn(params: {
beforeDelete: prepared.context.sessionWorkAdmission.release,
reason: "cron-delete-after-run-finally",
});
cronRunSessionCleanupAttempted = cleanupOutcome !== "not-requested";
}
} finally {
// Release runtime references after the run completes (success or failure).
@@ -747,10 +747,12 @@ describe("node worker transfer client", () => {
await expect(fs.readFile(path.join(workspaceDir, "tracked.txt"), "utf8")).resolves.toBe(
changed ? "changed on gateway\n" : "tracked from gateway\n",
);
expect((await fs.stat(path.join(workspaceDir, "tracked.txt"))).mode & 0o777).toBe(
changed ? 0o755 : 0o644,
);
expect((await fs.stat(path.join(workspaceDir, "script.sh"))).mode & 0o777).toBe(0o755);
if (process.platform !== "win32") {
expect((await fs.stat(path.join(workspaceDir, "tracked.txt"))).mode & 0o777).toBe(
changed ? 0o755 : 0o644,
);
expect((await fs.stat(path.join(workspaceDir, "script.sh"))).mode & 0o777).toBe(0o755);
}
await expect(fs.readlink(path.join(workspaceDir, "tracked-link"))).resolves.toBe(
changed ? "script.sh" : "tracked.txt",
);
+14 -2
View File
@@ -751,7 +751,18 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
expect(mocks.resolvePluginProvidersCore).not.toHaveBeenCalled();
});
it("defaults missing manifest model costs for static discovery entries", async () => {
it.each([
{
name: "missing",
cost: undefined,
expectedCost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
},
{
name: "partial",
cost: { input: 3, output: 15, cacheRead: 0.3 },
expectedCost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 },
},
])("defaults only $name manifest model cost components", async ({ cost, expectedCost }) => {
mocks.resolveDiscoveredProviderPluginIds.mockReturnValue(["anthropic"]);
mocks.loadPluginMetadataSnapshot.mockReturnValue({
index: { plugins: [] },
@@ -772,6 +783,7 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
input: ["text"],
contextWindow: 200000,
maxTokens: 64000,
...(cost ? { cost } : {}),
},
],
},
@@ -799,7 +811,7 @@ describe("resolvePluginDiscoveryProvidersRuntime", () => {
models: [
expect.objectContaining({
id: "claude-sonnet-4-6",
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
cost: expectedCost,
}),
],
}),
+6 -19
View File
@@ -114,26 +114,13 @@ function hasProviderAuthEnvCredential(
function modelDefinitionCostFromManifestRow(
row: NormalizedModelCatalogRow,
): ModelDefinitionConfig["cost"] {
if (
!row.cost ||
row.cost.input === undefined ||
row.cost.output === undefined ||
row.cost.cacheRead === undefined ||
row.cost.cacheWrite === undefined
) {
return {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
};
}
const cost = row.cost;
return {
input: row.cost.input,
output: row.cost.output,
cacheRead: row.cost.cacheRead,
cacheWrite: row.cost.cacheWrite,
...(row.cost.tieredPricing ? { tieredPricing: row.cost.tieredPricing } : {}),
input: cost?.input ?? 0,
output: cost?.output ?? 0,
cacheRead: cost?.cacheRead ?? 0,
cacheWrite: cost?.cacheWrite ?? 0,
...(cost?.tieredPricing ? { tieredPricing: cost.tieredPricing } : {}),
};
}