Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ee38c78
feat(usage): preserve model usage basis in projections
me2seeks Aug 10, 2026
e0f5a5d
fix(desktop): read usage from Runtime Host
me2seeks Aug 10, 2026
555ad5f
fix(desktop): qualify incomplete usage buckets
me2seeks Aug 10, 2026
2836018
fix(desktop): clarify incomplete usage totals
me2seeks Aug 10, 2026
580c19b
test(desktop): preserve Host-backed Usage contracts
me2seeks Aug 12, 2026
4794ccc
chore(storage): apply current formatting
me2seeks Aug 12, 2026
aa95537
fix(runtime-host): fence usage projections by revision
me2seeks Aug 13, 2026
dba39bd
fix(runtime): normalize legacy token totals
me2seeks Aug 13, 2026
05a5597
fix(runtime-host): repair Usage projections before fencing reads
me2seeks Aug 17, 2026
efa829a
fix(storage): record total-token provenance explicitly
me2seeks Aug 17, 2026
216ca89
test(desktop): seed the Usage settings fixture through Host stores
me2seeks Aug 17, 2026
4ad360c
fix(desktop): surface custom pricing rows in Host-backed Usage
me2seeks Aug 17, 2026
adde44e
fix(storage): bound the usage revision settle wait
me2seeks Aug 18, 2026
e73a033
test(storage): stop the usage writer on every exit path
me2seeks Aug 18, 2026
aff9101
fix(desktop): preserve Usage Host identity
me2seeks Aug 18, 2026
a96bb77
fix(runtime-host): advance compatibility epoch for usage revision
me2seeks Aug 19, 2026
ba42718
fix(usage): share repair pass across views
me2seeks Aug 19, 2026
7107b2f
fix(usage): preserve total-token provenance
me2seeks Aug 19, 2026
60cd6eb
fix(runtime-host): advance usage compatibility epoch
me2seeks Aug 19, 2026
07620fe
fix(usage): bound snapshot reads and pin repair across pages
me2seeks Aug 20, 2026
e5495a2
fix(usage): complete the rebase onto current main's Usage authority
me2seeks Aug 23, 2026
ebc4df9
fix(desktop,runtime-host): scope usage repair and key usage load state
me2seeks Aug 25, 2026
2e2503d
fix: repair build after rebase onto main
me2seeks Aug 25, 2026
dce0a2c
fix: keep storage entrypoints stable for usage fixture
me2seeks Aug 25, 2026
1cae0c5
fix: align settings-store with main for sqlite entrypoint test
me2seeks Aug 25, 2026
688ef93
fix(storage): update sqlite entrypoint allowlist after moving usage s…
me2seeks Aug 25, 2026
71790a0
chore: retrigger CI for fixture flake
me2seeks Aug 25, 2026
565ad71
chore: retrigger CI (2nd) for fixture flake
me2seeks Aug 25, 2026
855fba6
chore: retrigger CI (3rd) for fixture flake
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