Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions apps/desktop/src/main/__tests__/use-session-trace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function trace(sessionId: string): SessionTrace {

interface TraceHarness {
reads: string[];
contextReads: string[];
emit: (event: SessionEvent) => void;
subscriptions: number;
unsubscribes: number;
Expand All @@ -47,6 +48,7 @@ function installMakaBridge(): TraceHarness {
const handlers = new Set<(event: SessionEvent) => void>();
const harness: TraceHarness = {
reads: [],
contextReads: [],
emit: (event) => {
for (const handler of [...handlers]) handler(event);
},
Expand All @@ -61,6 +63,13 @@ function installMakaBridge(): TraceHarness {
harness.reads.push(sessionId);
return { ok: true, data: trace(sessionId) };
},
// The hook reads the context snapshot on the same signal (#2323). It
// is counted separately: the assertions below are about how often the
// TRACE is re-read, and an enrichment read must not move them.
context: async (sessionId: string) => {
harness.contextReads.push(sessionId);
return { ok: true as const, data: { status: 'unavailable' as const, reason: 'no_completed_request' as const } };
},
},
sessions: {
subscribeEvents: (_sessionId: string, handler: (event: SessionEvent) => void) => {
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/main/runtime-host-renderer-ipc-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ function request(
value: unknown,
): Promise<unknown> {
switch (operation) {
case 'context.diagnostics.query':
return client.request(operation, HOST_OPERATION_SPECS[operation].decodeInput(value));
case 'daily-review.mutate':
return client.request(operation, HOST_OPERATION_SPECS[operation].decodeInput(value));
case 'daily-review.query':
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/preload/bridge-contract.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import type {
RendererRuntimeHostQueryOperation,
} from './runtime-host-renderer-operations.js';
import type { SessionTrace } from '@maka/core/session-trace';
import type { ContextDiagnosticsResult } from '@maka/runtime-host/protocol';
import type { TestProxyInput } from '@maka/core/settings/network-settings';
import type { ExternalSessionImportIpcResult } from './external-session-import-result.js';
import type {
Expand Down Expand Up @@ -803,6 +804,8 @@ export interface MakaBridge {
inspector: {
/** Read-only per-session causal trace (#1625). */
trace(sessionId: string): Promise<Result<SessionTrace>>;
/** What the session's context is made of right now (#2323). */
context(sessionId: string): Promise<Result<ContextDiagnosticsResult>>;
};
webSearch: {
query(input: {
Expand Down
15 changes: 15 additions & 0 deletions apps/desktop/src/preload/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import {
isSessionTrace,
type SessionTrace,
} from '@maka/core/session-trace';
import type { ContextDiagnosticsResult } from '@maka/runtime-host/protocol';
import {
DAILY_REVIEW_RANGES,
normalizeDailyReviewConfig,
Expand Down Expand Up @@ -1422,6 +1423,20 @@ const makaBridge = {
trace(sessionId: string): Promise<Result<SessionTrace>> {
return bridgeResult(() => loadSessionTrace(sessionId), 'INSPECTOR_TRACE_FAILED');
},
/**
* What the session's context is made of right now (#2323).
*
* A different question from "what happened in this session", and it has
* its own typed owner on the Host — the same snapshot `/context` prints.
* The Inspector asks that owner rather than widening the trace, so the two
* surfaces cannot drift into two implementations of one fact.
*/
context(sessionId: string): Promise<Result<ContextDiagnosticsResult>> {
return bridgeResult(
() => runtimeHost.query('context.diagnostics.query', { sessionId }),
'INSPECTOR_CONTEXT_FAILED',
);
},
},
dailyReview: {
day(offsetDays: number, daySpan?: number): Promise<Result<DailyReviewSummary>> {
Expand Down
4 changes: 4 additions & 0 deletions apps/desktop/src/preload/runtime-host-renderer-operations.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { OperationSpecMap } from '@maka/runtime-host/protocol';

export const RENDERER_RUNTIME_HOST_QUERY_OPERATIONS = [
// Read-only, session-scoped, and already the owner of "what is the context
// made of" for `/context` (#1580, #2323). Admitted on the same terms as the
// inspect query beside it: it reads a projection and writes nothing.
'context.diagnostics.query',
'daily-review.query',
'execution.inspect.query',
'scheduled-task.query',
Expand Down
54 changes: 54 additions & 0 deletions apps/desktop/src/renderer/locales/conversation-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,32 @@ export interface DesktopConversationCopy {
cacheHit: string;
/** Heading over the causal record. */
timelineTab: string;
/**
* What filled the context, under the bar that says how full it is.
*
* Kept verbally separate from the bar on purpose: these are estimates
* over serialized bytes and do not sum to the provider-reported prompt
* (#2323), so the heading says estimate and every figure carries a `≈`.
*/
composition: {
title: string;
/** States the unit and its authority, once, under the heading. */
basis: string;
part: {
system_instructions: string;
tool_definitions: string;
messages: string;
other: string;
};
/** Heading over the per-tool rows. */
tools: string;
/** The tools below the visible rows, folded into one. */
remainingTools: (count: number) => string;
/** Tool schemas the payload never named. */
unlabelled: string;
/** The metered call carried no capture — a gap, not an empty prompt. */
unrecorded: string;
};
};
};
quoteCompanion: {
Expand Down Expand Up @@ -479,6 +505,20 @@ const COPY = {
},
cacheHit: '缓存命中率',
timelineTab: '时间轴',
composition: {
title: '构成估算',
basis: '按请求字节估算,非模型报告的 token',
part: {
system_instructions: '系统提示',
tool_definitions: '工具定义',
messages: '对话记录',
other: '其他参数',
},
tools: '按工具',
remainingTools: (count) => `其余 ${count} 个工具`,
unlabelled: '未命名的工具',
unrecorded: '这次调用没有留下构成记录',
},
},
},
quoteCompanion: {
Expand Down Expand Up @@ -666,6 +706,20 @@ const COPY = {
},
cacheHit: 'Cache hit rate',
timelineTab: 'Timeline',
composition: {
title: 'Estimated composition',
basis: 'Estimated from request bytes, not provider-reported tokens',
part: {
system_instructions: 'System instructions',
tool_definitions: 'Tool definitions',
messages: 'Messages',
other: 'Other options',
},
tools: 'By tool',
remainingTools: (count) => `${count} more tool${count === 1 ? '' : 's'}`,
unlabelled: 'Unnamed tools',
unrecorded: 'This call left no composition on record',
},
},
},
quoteCompanion: {
Expand Down
Loading
Loading