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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"test:watch": "vitest"
},
"dependencies": {
"@ellipsis-dev/sdk": "^0.8.1",
"@ellipsis-dev/sdk": "^0.10.0",
"chalk": "^5.6.2",
"cli-table3": "^0.6.5",
"commander": "^12.1.0",
Expand Down
4 changes: 2 additions & 2 deletions src/commands/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ export async function runConnect(
minRenderFeedSeq: showRecords ? 0 : store.cursor,
sessionUrl: url,
initialNotice: notice,
// The session's one model, fixed at creation (backend tokens_model).
model: typeof session.tokens_model === 'string' ? session.tokens_model : null,
// The session's one model, fixed at creation.
model: session.tokens?.model || null,
configName: config,
exitState,
}),
Expand Down
25 changes: 10 additions & 15 deletions src/commands/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,7 @@ export function registerSession(program: Command): void {
s.status,
s.source ?? '-',
formatTs(s.created_at),
usdFromMillicents(
s.cost_tokens + s.cost_sandbox_cpu + s.cost_sandbox_memory + s.cost_fee,
),
usdFromMillicents(s.cost?.total ?? 0),
]),
)
})
Expand Down Expand Up @@ -494,7 +492,7 @@ export function registerSession(program: Command): void {
}
}
console.log(
'\nInspect one: agent session get <session-id>. Full log: agent session log <session-id>',
'\nInspect one: agent session get <session-id>. Full history: agent session export <session-id>',
)
})
},
Expand Down Expand Up @@ -531,11 +529,12 @@ export function registerSession(program: Command): void {
apiRoutes(
alsoKnownAs(
session
.command('log <session-id>')
.description("Download a session's complete archived log to stdout or a file"),
.command('export <session-id>')
.description("Download a session's complete archived history to stdout or a file"),
'log',
'logs',
),
'GET /sessions/{id}/log',
'GET /sessions/{id}/export',
)
.option('-o, --output <path>', 'write to a file instead of stdout')
.option('--gzip', 'keep the concatenated .jsonl.gz bytes as-is (skip gunzip)')
Expand All @@ -550,7 +549,7 @@ export function registerSession(program: Command): void {
},
) => {
await runAction(async () => {
const manifest = await api().sessions.log(sessionId)
const manifest = await api().sessions.export(sessionId)
if (opts.json) {
printJson(manifest)
return
Expand Down Expand Up @@ -1057,12 +1056,8 @@ function printSessionSummary(s: AgentSession): void {
if (s.config_id) console.log(`config: ${s.config_id}`)
console.log(`created: ${s.created_at}`)
console.log(`updated: ${s.updated_at}`)
console.log(`tokens: ${s.tokens_total.toLocaleString()}`)
console.log(
`cost: ${usdFromMillicents(
s.cost_tokens + s.cost_sandbox_cpu + s.cost_sandbox_memory + s.cost_fee,
)}`,
)
console.log(`tokens: ${(s.tokens?.total ?? 0).toLocaleString()}`)
console.log(`cost: ${usdFromMillicents(s.cost?.total ?? 0)}`)
const keys = Object.keys(s.metadata ?? {})
if (keys.length) {
console.log('metadata:')
Expand Down Expand Up @@ -1230,7 +1225,7 @@ export function formatSearchResult(
now: Date = new Date(),
): string[] {
const s = result.session
const author = s.attribution_id ? users[String(s.attribution_id)]?.login : undefined
const author = s.attribution?.id ? users[String(s.attribution.id)]?.login : undefined
const header = [
s.id,
s.status,
Expand Down
14 changes: 7 additions & 7 deletions src/lib/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ function trimZero(s: string): string {
}

// The nav row's right-hand metadata: how much work the agent did (tokens,
// spend) and when it last moved. Spend is the sum of the four millicent cost
// columns, the same total the chat footer shows. A just-started session drops
// the empty bits rather than showing "0 · $0.00". No source tag: the nav lists
// cloud sessions only, so it would read the same on every row.
// spend) and when it last moved. Spend is the server's millicent total, the
// same total the chat footer shows. A just-started session drops the empty
// bits rather than showing "0 · $0.00". No source tag: the nav lists cloud
// sessions only, so it would read the same on every row.
export function rowMeta(session: AgentSession, now: Date = new Date()): string {
const bits: string[] = []
if (session.tokens_total > 0) bits.push(compactTokens(session.tokens_total))
const millicents =
session.cost_tokens + session.cost_sandbox_cpu + session.cost_sandbox_memory + session.cost_fee
const tokens = session.tokens?.total ?? 0
if (tokens > 0) bits.push(compactTokens(tokens))
const millicents = session.cost?.total ?? 0
if (millicents > 0) bits.push(`$${(millicents / 100_000).toFixed(2)}`)
bits.push(shortAge(lastEventAt(session), now))
return bits.join(' · ')
Expand Down
4 changes: 2 additions & 2 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ type S = components['schemas']
// --------------------------- sessions & records ---------------------------

export type AgentSession = S['Session']
export type AgentSessionSource = S['AgentSessionSource']
export type AgentSessionStatus = S['AgentSessionStatus']
export type AgentSessionSource = S['SessionSource']
export type AgentSessionStatus = S['SessionStatus']
export type SessionState = S['SessionState']
export type SessionSurface = S['SessionSurface']
export type SessionPrompting = S['SessionPrompting']
Expand Down
7 changes: 1 addition & 6 deletions src/ui/ConnectApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,7 @@ export function ConnectApp(props: ConnectAppProps): React.ReactElement {
[snapshot.records],
)
const serverCostUsd = snapshot.session
? usdNumberFromMillicents(
snapshot.session.cost_tokens +
snapshot.session.cost_sandbox_cpu +
snapshot.session.cost_sandbox_memory +
snapshot.session.cost_fee,
)
? usdNumberFromMillicents(snapshot.session.cost?.total ?? 0)
: null

// The sandbox startup timeline, derived from the lifecycle records of the
Expand Down
13 changes: 3 additions & 10 deletions src/ui/SessionsApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export function SessionsApp(props: SessionsAppProps): React.ReactElement {
store,
canSend: c.canSend,
notice: [notice, c.reason].filter(Boolean).join(' · ') || null,
model: typeof session.tokens_model === 'string' ? session.tokens_model : null,
model: session.tokens?.model || null,
configName: configName ?? session.config_id ?? null,
url: sessionUrl(appBase, customerLogin, sessionId),
}
Expand Down Expand Up @@ -758,15 +758,8 @@ function useHeaderMeta(
)
if (mainPane.type !== 'chat' || !entry) return null
const session = snapshot?.session as FrameSession | undefined | null
const costUsd = session
? usdNumberFromMillicents(
session.cost_tokens +
session.cost_sandbox_cpu +
session.cost_sandbox_memory +
session.cost_fee,
)
: 0
const tokens = session?.tokens_total ?? 0
const costUsd = session ? usdNumberFromMillicents(session.cost?.total ?? 0) : 0
const tokens = session?.tokens?.total ?? 0
const id = mainPane.sessionId
const line = (idText: string, model: string | null | undefined): string =>
[
Expand Down
30 changes: 18 additions & 12 deletions test/connect-render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,15 @@ function costTick(store: SessionTranscriptStore, cents: number): void {
session: {
id: 'session_render',
status: 'waiting',
cost_tokens: cents,
cost_sandbox_cpu: 0,
cost_sandbox_memory: 0,
cost_fee: 0,
tokens_total: cents,
tokens_model: 'claude-fable-5',
cost: { llm: cents, sandbox_cpu: 0, sandbox_memory: 0, fee: 0, total: cents },
tokens: {
input: 0,
output: 0,
cache_read: 0,
cache_creation: 0,
total: cents,
model: 'claude-fable-5',
},
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any)
Expand Down Expand Up @@ -108,12 +111,15 @@ function seededStore(records: Record<string, unknown>[], status: string) {
const session = {
id: 'session_render',
status,
cost_tokens: 0,
cost_sandbox_cpu: 0,
cost_sandbox_memory: 0,
cost_fee: 0,
tokens_total: 0,
tokens_model: 'claude-fable-5',
cost: { llm: 0, sandbox_cpu: 0, sandbox_memory: 0, fee: 0, total: 0 },
tokens: {
input: 0,
output: 0,
cache_read: 0,
cache_creation: 0,
total: 0,
model: 'claude-fable-5',
},
}
store.ingest({
type: 'snapshot',
Expand Down
17 changes: 4 additions & 13 deletions test/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,9 @@ function session(overrides: Partial<AgentSession> = {}): AgentSession {
source: 'api',
harness: 'claude_code',
prompting: { enabled: true },
resolved_budget_cents: 0,
resolved_budget_source: 'system',
cost_tokens: 0,
cost_sandbox_cpu: 0,
cost_sandbox_memory: 0,
cost_fee: 0,
tokens_total: 0,
tokens_input: 0,
tokens_output: 0,
tokens_cache_read: 0,
tokens_cache_creation: 0,
tokens_model: '',
budget: { cents: 0, source: 'system' },
cost: { llm: 0, sandbox_cpu: 0, sandbox_memory: 0, fee: 0, total: 0 },
tokens: { input: 0, output: 0, cache_read: 0, cache_creation: 0, total: 0, model: '' },
metadata: {},
...overrides,
}
Expand Down Expand Up @@ -144,7 +135,7 @@ describe('formatSearchResult', () => {

it('renders header, author, age, matched arms, and the recap snippet', () => {
const result: SessionSearchResult = {
session: session({ attribution_id: '5201153' }),
session: session({ attribution: { id: '5201153', type: 'github_user' } }),
matched: ['recap', 'similar'],
recap_snippet: 'looked into the shift trade webhook retries',
record_hits: [],
Expand Down
22 changes: 5 additions & 17 deletions test/sessions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,9 @@ function session(overrides: Partial<AgentSession>): AgentSession {
source: 'api',
harness: 'claude_code',
prompting: { enabled: true },
resolved_budget_cents: 0,
resolved_budget_source: 'system',
cost_tokens: 0,
cost_sandbox_cpu: 0,
cost_sandbox_memory: 0,
cost_fee: 0,
tokens_total: 0,
tokens_input: 0,
tokens_output: 0,
tokens_cache_read: 0,
tokens_cache_creation: 0,
tokens_model: '',
budget: { cents: 0, source: 'system' },
cost: { llm: 0, sandbox_cpu: 0, sandbox_memory: 0, fee: 0, total: 0 },
tokens: { input: 0, output: 0, cache_read: 0, cache_creation: 0, total: 0, model: '' },
metadata: {},
...overrides,
}
Expand Down Expand Up @@ -173,11 +164,8 @@ describe('rowMeta', () => {

it('reads tokens, spend, and age', () => {
const s = session({
tokens_total: 84_200,
cost_tokens: 30_000,
cost_sandbox_cpu: 10_000,
cost_sandbox_memory: 2_000,
cost_fee: 0,
tokens: { input: 0, output: 0, cache_read: 0, cache_creation: 0, total: 84_200, model: '' },
cost: { llm: 30_000, sandbox_cpu: 10_000, sandbox_memory: 2_000, fee: 0, total: 42_000 },
updated_at: '2026-07-23T11:58:00Z',
} as never)
expect(rowMeta(s, now)).toBe('84.2k · $0.42 · 2m ago')
Expand Down