pending_changes.agent stamped at every queue site (native -> 'boocode', dispatched external -> task.agent, manual RightRail -> NULL) + flows through listPending. New GET /api/sessions/:id/agent-sessions -> [{agent,status,has_session,last_active_at}] per (chat,agent). opencode warm server consumes session.next.step.ended, accumulating input_tokens/output_tokens/cost onto agent_sessions (new idempotent columns) via a pure opencode-usage.ts mapper. Tests: agent-sessions.routes (3) + opencode-usage (6); tsc clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { stepEndedToUsage } from '../opencode-usage.js';
|
|
|
|
describe('stepEndedToUsage (U.6)', () => {
|
|
it('folds cache read+write into input and reasoning into output', () => {
|
|
const u = stepEndedToUsage({
|
|
cost: 0.0123,
|
|
tokens: { input: 100, output: 50, reasoning: 20, cache: { read: 10, write: 5 } },
|
|
});
|
|
expect(u).toEqual({ input: 115, output: 70, cost: 0.0123 });
|
|
});
|
|
|
|
it('handles a step with no cache and no reasoning', () => {
|
|
const u = stepEndedToUsage({
|
|
cost: 0,
|
|
tokens: { input: 8, output: 4, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
});
|
|
expect(u).toEqual({ input: 8, output: 4, cost: 0 });
|
|
});
|
|
|
|
it('is defensive against a missing tokens block', () => {
|
|
const u = stepEndedToUsage({ cost: 0.5 } as never);
|
|
expect(u).toEqual({ input: 0, output: 0, cost: 0.5 });
|
|
});
|
|
|
|
it('is defensive against undefined props', () => {
|
|
expect(stepEndedToUsage(undefined)).toEqual({ input: 0, output: 0, cost: 0 });
|
|
});
|
|
|
|
it('drops NaN / negative noise to zero rather than poisoning the accumulated total', () => {
|
|
const u = stepEndedToUsage({
|
|
cost: Number.NaN,
|
|
tokens: {
|
|
input: -5,
|
|
output: Number.NaN,
|
|
reasoning: 3,
|
|
cache: { read: Number.POSITIVE_INFINITY, write: 2 },
|
|
},
|
|
});
|
|
// input: (-5→0) + (Inf→0) + 2 = 2; output: (NaN→0) + 3 = 3; cost: NaN→0
|
|
expect(u).toEqual({ input: 2, output: 3, cost: 0 });
|
|
});
|
|
|
|
it('rounds fractional token counts', () => {
|
|
const u = stepEndedToUsage({
|
|
cost: 1.5,
|
|
tokens: { input: 10.6, output: 4.4, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
});
|
|
expect(u).toEqual({ input: 11, output: 4, cost: 1.5 });
|
|
});
|
|
});
|