Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ec43995
feat(usage): preserve model usage basis in projections
me2seeks Aug 10, 2026
41667ac
fix(desktop): read usage from Runtime Host
me2seeks Aug 10, 2026
850e847
fix(desktop): qualify incomplete usage buckets
me2seeks Aug 10, 2026
e3888d8
fix(desktop): clarify incomplete usage totals
me2seeks Aug 10, 2026
92f7b37
test(desktop): preserve Host-backed Usage contracts
me2seeks Aug 12, 2026
75362f0
chore(storage): apply current formatting
me2seeks Aug 12, 2026
453eb9c
fix(runtime-host): fence usage projections by revision
me2seeks Aug 13, 2026
338212c
fix(runtime): normalize legacy token totals
me2seeks Aug 13, 2026
733132c
fix(runtime-host): repair Usage projections before fencing reads
me2seeks Aug 17, 2026
cf0081e
fix(storage): record total-token provenance explicitly
me2seeks Aug 17, 2026
c4f697d
test(desktop): seed the Usage settings fixture through Host stores
me2seeks Aug 17, 2026
a1c13d0
fix(desktop): surface custom pricing rows in Host-backed Usage
me2seeks Aug 17, 2026
04cc250
fix(storage): bound the usage revision settle wait
me2seeks Aug 18, 2026
6f1b5ee
test(storage): stop the usage writer on every exit path
me2seeks Aug 18, 2026
46d91d7
fix(desktop): preserve Usage Host identity
me2seeks Aug 18, 2026
fdd1740
fix(runtime-host): advance compatibility epoch for usage revision
me2seeks Aug 19, 2026
0b3cb2f
fix(usage): share repair pass across views
me2seeks Aug 19, 2026
dae9dfb
fix(usage): preserve total-token provenance
me2seeks Aug 19, 2026
edb7d29
fix(runtime-host): advance usage compatibility epoch
me2seeks Aug 19, 2026
6870012
fix(usage): bound snapshot reads and pin repair across pages
me2seeks Aug 20, 2026
ab2b75e
fix(usage): complete the rebase onto current main's Usage authority
me2seeks Aug 23, 2026
27cfe89
fix(desktop,runtime-host): scope usage repair and key usage load state
me2seeks Aug 25, 2026
ef4efb0
fix: repair build after rebase onto main
me2seeks Aug 25, 2026
99355e3
fix: keep storage entrypoints stable for usage fixture
me2seeks Aug 25, 2026
fb7b6b6
fix: align settings-store with main for sqlite entrypoint test
me2seeks Aug 25, 2026
f764ec4
fix(storage): update sqlite entrypoint allowlist after moving usage s…
me2seeks Aug 25, 2026
8847b3a
chore: retrigger CI for fixture flake
me2seeks Aug 25, 2026
ea919ec
chore: retrigger CI (2nd) for fixture flake
me2seeks Aug 25, 2026
1d20fc5
chore: retrigger CI (3rd) for fixture flake
me2seeks Aug 25, 2026
17e5fdc
fix(desktop): make settings-usage seed idempotent
me2seeks Aug 25, 2026
27f2dc8
Merge remote-tracking branch 'origin/main' into fix/2128-host-backed-…
me2seeks Aug 25, 2026
63d4697
Merge remote-tracking branch 'origin/main' into fix/2128-host-backed-…
me2seeks Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions apps/desktop/src/main/__tests__/runtime-host-desktop-candidate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {
type DesktopRuntimeHostCandidateStartInput,
} from '../runtime-host-desktop-candidate.js';
import { RuntimeHostSessionObservationRegistry } from '../runtime-host-session-observation-registry.js';
import { registerRuntimeHostUsageIpc } from '../runtime-host-usage-ipc-main.js';
import { desktopSessionResourceKey } from '../../shared/runtime-host-identity.js';

const TEST_HOST_ID = 'a'.repeat(64);
Expand Down Expand Up @@ -274,6 +275,43 @@ test('rejects a stale target generation when two profiles share one Host', async
await secondCandidate.close();
});

test('routes default and ranged Usage reads through the selected Host scope', async () => {
const ipc = ipcHarness();
const usageQueries: unknown[] = [];
const host = connectionHarness('usage-scope', { usageQueries });
const candidate = await createDesktopRuntimeHostCandidate(host.connection, {
...deps(ipc),
registerClientIpc: (client, scopedIpc, _controls, _target, scope) => {
registerRuntimeHostUsageIpc({
client,
ipcMain: scopedIpc,
host: scope,
now: () => 2 * 24 * 60 * 60 * 1_000,
sendToRenderer() {},
});
},
});

const defaultStats = await ipc.invokeFor(TEST_HOST_ID, 'settings:usageStats');
const rangedStats = await ipc.invokeFor(TEST_HOST_ID, 'settings:usageStats', 'all');

assert.equal((defaultStats as { summary: { totalRequests: number } }).summary.totalRequests, 0);
assert.equal((rangedStats as { summary: { totalRequests: number } }).summary.totalRequests, 0);
const summaryRanges = usageQueries.flatMap((input) => {
const value = input as { kind?: unknown; query?: { range?: { from: number; to: number } } };
return value.kind === 'summary' && value.query?.range ? [value.query.range] : [];
});
assert.equal(summaryRanges.length, 2);
assert.equal(summaryRanges[0]!.to - summaryRanges[0]!.from, 24 * 60 * 60 * 1_000);
assert.equal(summaryRanges[1]!.from, 0);
await assert.rejects(
() => ipc.invokeWithoutScope('settings:usageStats'),
/missing its Host identity/,
);

await candidate.close();
});

test('tears down the whole candidate when the Host connection closes', async () => {
const ipc = ipcHarness();
const host = connectionHarness('closed');
Expand Down Expand Up @@ -817,6 +855,11 @@ function ipcHarness(onSend?: (channel: string, payload: unknown) => void) {
async invoke(channel: string, ...args: unknown[]): Promise<unknown> {
return this.invokeFor(TEST_HOST_ID, channel, ...args);
},
async invokeWithoutScope(channel: string, ...args: unknown[]): Promise<unknown> {
const handler = handlers.get(channel);
assert.ok(handler, `missing handler: ${channel}`);
return handler({ sender } as never, ...args);
},
async invokeFor(hostId: string, channel: string, ...args: unknown[]): Promise<unknown> {
return this.invokeForTarget(TEST_TARGET_EPOCH, hostId, channel, ...args);
},
Expand Down Expand Up @@ -898,6 +941,7 @@ function connectionHarness(
activeAssistantStreams?: readonly SessionAssistantStreamIdentity[];
subscriptionError?: Error;
runtimeResourcePty?: ReturnType<typeof ptySnapshot>;
usageQueries?: unknown[];
} = {},
) {
let resolveClosed: (() => void) | undefined;
Expand Down Expand Up @@ -1003,6 +1047,81 @@ function connectionHarness(
resolveTurnStarted?.();
return {};
}
if (operation === 'usage.query') {
options.usageQueries?.push(input);
const query = input as {
kind: 'summary' | 'buckets' | 'logs';
source?: 'llm' | 'tool';
query: { range: { from: number; to: number } };
};
const emptyProvenance = {
coverage: {
attempts: 0,
pricedAttempts: 0,
unpricedAttempts: 0,
usageReportedAttempts: 0,
usagePartialAttempts: 0,
usageMissingAttempts: 0,
},
legacyRecords: 0,
unreadableRecords: 0,
pendingRepairs: 0,
};
if (query.kind === 'summary') {
return {
kind: 'summary',
revision: 1,
summary: {
range: query.query.range,
totalRequests: 0,
totalCostUsd: 0,
totalTokens: {
input: 0,
output: 0,
cacheMiss: 0,
cacheRead: 0,
cacheWrite: 0,
reasoning: 0,
total: 0,
},
cacheHitRequests: 0,
cacheCreateRequests: 0,
errorRequests: 0,
},
provenance: emptyProvenance,
};
}
if (query.kind === 'buckets') {
return {
kind: 'buckets',
revision: 1,
buckets: [],
offset: 0,
total: 0,
nextOffset: null,
provenance: emptyProvenance,
};
}
return {
kind: 'logs',
revision: 1,
source: query.source,
rows: [],
offset: 0,
total: 0,
nextOffset: null,
...(query.source === 'llm' ? { provenance: emptyProvenance } : {}),
};
}
if (operation === 'pricing.query') {
return {
kind: 'page',
revision: 1,
offset: 0,
entries: [],
nextOffset: null,
};
}
throw new Error(`Unexpected operation: ${operation}`);
},
openSessionSubscription: async ({ sessionId }: { sessionId: string }) => {
Expand Down
Loading
Loading