fix: synchronize live worker context metadata
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -381,7 +381,7 @@ summary: string,
|
||||
* Full tool output. Absent when the tool chose to return
|
||||
* summary-only, or when the result was pruned.
|
||||
*/
|
||||
output?: string | null, disposition?: ToolResultDisposition | null, is_error: boolean, } } | { "event": "usage", "data": { input_tokens: number | null, output_tokens: number | null, cache_read_input_tokens?: number | null, } } | { "event": "run_end", "data": { result: RunResult, } } | { "event": "error", "data": { code: ErrorCode, message: string, } } | { "event": "snapshot", "data": { session: SessionSnapshot, greeting: Greeting,
|
||||
output?: string | null, disposition?: ToolResultDisposition | null, is_error: boolean, } } | { "event": "usage", "data": { input_tokens: number | null, output_tokens: number | null, cache_read_input_tokens?: number | null, } } | { "event": "context_usage", "data": { usage?: ContextUsage | null, } } | { "event": "run_end", "data": { result: RunResult, } } | { "event": "error", "data": { code: ErrorCode, message: string, } } | { "event": "snapshot", "data": { session: SessionSnapshot, greeting: Greeting,
|
||||
/**
|
||||
* Full revisioned live execution state. `Stopped` remains Runtime
|
||||
* catalog authority and is deliberately not represented here.
|
||||
|
||||
@@ -39,4 +39,15 @@ test("renders honest unavailable state instead of a fabricated zero percent", as
|
||||
});
|
||||
expect(view.container.textContent).toContain("claude · 8.2k token budget");
|
||||
expect(view.container.textContent).toContain("Context ~12.0k / 64.0k (19%)");
|
||||
|
||||
await view.rerender({
|
||||
metadata: {
|
||||
model: "claude",
|
||||
reasoning: { kind: "budget_tokens", budget_tokens: -1 },
|
||||
contextWindow: 64_000,
|
||||
contextTokens: 12_000,
|
||||
contextSource: "estimated",
|
||||
},
|
||||
});
|
||||
expect(view.container.textContent).toContain("claude · dynamic token budget");
|
||||
});
|
||||
|
||||
@@ -224,14 +224,17 @@ Deno.test("snapshot and usage keep current context separate from run traffic", (
|
||||
cache_read_input_tokens: 140_000,
|
||||
output_tokens: 2_000,
|
||||
} }, observedAtMs: 2 },
|
||||
{ eventId: "context", event: { event: "context_usage", data: {
|
||||
usage: { tokens: 158_000, source: "estimated" },
|
||||
} }, observedAtMs: 3 },
|
||||
]);
|
||||
|
||||
assertEquals(projection.workerMetadata, {
|
||||
model: "gpt-6-astra",
|
||||
reasoning: { kind: "effort", effort: "high" },
|
||||
contextWindow: 272_000,
|
||||
contextTokens: 150_000,
|
||||
contextSource: "measured",
|
||||
contextTokens: 158_000,
|
||||
contextSource: "estimated",
|
||||
});
|
||||
assertEquals(projection.runActivity.uploadTokens, 10_000);
|
||||
assertEquals(projection.runActivity.outputTokens, 2_000);
|
||||
@@ -279,6 +282,20 @@ Deno.test("unknown and destructive boundaries never fabricate context percentage
|
||||
]);
|
||||
assertEquals(projection.workerMetadata?.contextTokens, null);
|
||||
assertEquals(projection.workerMetadata?.contextSource, null);
|
||||
|
||||
projection = projectConsole([
|
||||
{ eventId: "live", event: live },
|
||||
{ eventId: "rewind", event: { event: "rewind_applied", data: {
|
||||
session: canonicalSession([]),
|
||||
input: [],
|
||||
summary: { truncated_to_entries: 0, discarded_entries: 1, tool_side_effect_warning: false },
|
||||
} } },
|
||||
{ eventId: "post-rewind-context", event: { event: "context_usage", data: {
|
||||
usage: { tokens: 12, source: "estimated" },
|
||||
} } },
|
||||
]);
|
||||
assertEquals(projection.workerMetadata?.contextTokens, 12);
|
||||
assertEquals(projection.workerMetadata?.contextSource, "estimated");
|
||||
});
|
||||
|
||||
Deno.test("large paste segments project compact artifact metadata", () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
CommandEvent,
|
||||
CommandSnapshot,
|
||||
CommandStreamSlice,
|
||||
ContextUsage,
|
||||
Event as ProtocolEvent,
|
||||
Greeting,
|
||||
InFlightBlock,
|
||||
@@ -256,6 +257,20 @@ function metadataWithUsage(
|
||||
};
|
||||
}
|
||||
|
||||
function metadataWithContextUsage(
|
||||
metadata: ConsoleWorkerMetadata | null,
|
||||
usage: ContextUsage | null | undefined,
|
||||
): ConsoleWorkerMetadata {
|
||||
const tokens = safeTokenCount(usage?.tokens);
|
||||
return {
|
||||
model: metadata?.model ?? null,
|
||||
reasoning: metadata?.reasoning ?? null,
|
||||
contextWindow: metadata?.contextWindow ?? null,
|
||||
contextTokens: tokens,
|
||||
contextSource: tokens === null ? null : usage?.source ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function metadataWithoutContext(
|
||||
metadata: ConsoleWorkerMetadata | null,
|
||||
): ConsoleWorkerMetadata | null {
|
||||
@@ -1127,6 +1142,12 @@ export function applyProtocolEvent(
|
||||
event.data.input_tokens,
|
||||
);
|
||||
break;
|
||||
case "context_usage":
|
||||
next.workerMetadata = metadataWithContextUsage(
|
||||
next.workerMetadata,
|
||||
event.data.usage,
|
||||
);
|
||||
break;
|
||||
case "error":
|
||||
if (hasDurableRunFailure(next, event.data.message)) break;
|
||||
next.lines.push(
|
||||
|
||||
@@ -58,6 +58,10 @@ Deno.test("worker metadata formats effort, budget, estimate, and unknown safely"
|
||||
);
|
||||
assertEquals(
|
||||
formatReasoning({ kind: "budget_tokens", budget_tokens: -1 }),
|
||||
"dynamic token budget",
|
||||
);
|
||||
assertEquals(
|
||||
formatReasoning({ kind: "budget_tokens", budget_tokens: -2 }),
|
||||
"reasoning unavailable",
|
||||
);
|
||||
assertEquals(formatContextSummary(null), "Context unavailable");
|
||||
|
||||
@@ -8,10 +8,13 @@ export function formatReasoning(reasoning: ReasoningConfig | null): string {
|
||||
const effort = reasoning.effort.trim();
|
||||
return effort.length > 0 ? effort : "reasoning unavailable";
|
||||
}
|
||||
if (
|
||||
!Number.isSafeInteger(reasoning.budget_tokens) ||
|
||||
reasoning.budget_tokens <= 0
|
||||
) {
|
||||
if (!Number.isSafeInteger(reasoning.budget_tokens)) {
|
||||
return "reasoning unavailable";
|
||||
}
|
||||
if (reasoning.budget_tokens === -1) {
|
||||
return "dynamic token budget";
|
||||
}
|
||||
if (reasoning.budget_tokens <= 0) {
|
||||
return "reasoning unavailable";
|
||||
}
|
||||
return `${formatRunTokens(reasoning.budget_tokens)} token budget`;
|
||||
|
||||
Reference in New Issue
Block a user