mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-19 16:13:56 +00:00
Keep OpenCode router calls without usage (#342)
Some checks are pending
CI / semgrep (push) Waiting to run
Some checks are pending
CI / semgrep (push) Waiting to run
This commit is contained in:
parent
06f69484f3
commit
c9487e7b0a
4 changed files with 57 additions and 3 deletions
|
|
@ -43,6 +43,11 @@
|
|||
child and grandchild agent sessions contribute token and tool usage under
|
||||
the discovered root session while still excluding child sessions from
|
||||
top-level discovery to avoid double counting.
|
||||
- **OpenCode router sessions with missing usage are still reported.**
|
||||
Some OpenCode router/provider combinations can persist assistant messages
|
||||
with text or tool activity but zero token and cost fields. The OpenCode
|
||||
parser now keeps those turns as zero-cost calls instead of dropping the
|
||||
session entirely. Closes #341.
|
||||
|
||||
## 0.9.9 - 2026-05-15
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ Per `<sessionId>:<messageId>`.
|
|||
token, and tool usage back to the root session.
|
||||
- Each message's `parts` are indexed; preserving the order matters for reasoning-token correctness.
|
||||
- Tokens are reported across `input`, `output`, `reasoning`, `cache.read`, and `cache.write`. Anthropic semantics.
|
||||
- Assistant messages with missing router usage are kept as zero-cost calls
|
||||
when their parts contain non-empty text or tool activity. Empty zero-usage
|
||||
assistant placeholders are still skipped.
|
||||
- External MCP tools are stored as `<server>_<tool>` names (for example
|
||||
`clickup_clickup_get_task`). The provider normalizes those to CodeBurn's
|
||||
canonical `mcp__<server>__<tool>` names before aggregation so shared MCP
|
||||
|
|
|
|||
|
|
@ -264,16 +264,19 @@ function createParser(
|
|||
cacheWrite: data.tokens?.cache?.write ?? 0,
|
||||
}
|
||||
|
||||
const msgParts = partsByMsg.get(msg.id) ?? []
|
||||
const toolParts = msgParts.filter((p) => p.type === 'tool' && normalizeToolName(p.tool))
|
||||
const hasTextOutput = msgParts.some((p) => p.type === 'text' && typeof p.text === 'string' && p.text.trim().length > 0)
|
||||
const hasActivity = hasTextOutput || toolParts.length > 0
|
||||
|
||||
const allZero =
|
||||
tokens.input === 0 &&
|
||||
tokens.output === 0 &&
|
||||
tokens.reasoning === 0 &&
|
||||
tokens.cacheRead === 0 &&
|
||||
tokens.cacheWrite === 0
|
||||
if (allZero && (data.cost ?? 0) === 0) continue
|
||||
if (allZero && (data.cost ?? 0) === 0 && !hasActivity) continue
|
||||
|
||||
const msgParts = partsByMsg.get(msg.id) ?? []
|
||||
const toolParts = msgParts.filter((p) => p.type === 'tool')
|
||||
const tools = toolParts
|
||||
.map((p) => normalizeToolName(p.tool))
|
||||
.filter(Boolean)
|
||||
|
|
|
|||
|
|
@ -469,6 +469,49 @@ skipUnlessSqlite('opencode provider - session parsing', () => {
|
|||
expect(calls).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('keeps zero-usage assistant messages when router responses contain text', async () => {
|
||||
const dbPath = createTestDb(tmpDir)
|
||||
withTestDb(dbPath, (db) => {
|
||||
insertSession(db, 'sess-1')
|
||||
insertMessage(db, 'msg-u1', 'sess-1', 1700000000000, { role: 'user' })
|
||||
insertPart(db, 'part-u1', 'msg-u1', 'sess-1', { type: 'text', text: 'use the configured router' })
|
||||
insertMessage(db, 'msg-a1', 'sess-1', 1700000001000, {
|
||||
role: 'assistant', modelID: 'edenai/router-model', cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
})
|
||||
insertPart(db, 'part-a1', 'msg-a1', 'sess-1', { type: 'text', text: 'router response text' })
|
||||
})
|
||||
|
||||
const calls = await collectCalls(createOpenCodeProvider(tmpDir), dbPath, 'sess-1')
|
||||
expect(calls).toHaveLength(1)
|
||||
expect(calls[0]!.model).toBe('edenai/router-model')
|
||||
expect(calls[0]!.inputTokens).toBe(0)
|
||||
expect(calls[0]!.outputTokens).toBe(0)
|
||||
expect(calls[0]!.costUSD).toBe(0)
|
||||
expect(calls[0]!.userMessage).toBe('use the configured router')
|
||||
})
|
||||
|
||||
it('keeps zero-usage assistant messages when router responses contain tool calls', async () => {
|
||||
const dbPath = createTestDb(tmpDir)
|
||||
withTestDb(dbPath, (db) => {
|
||||
insertSession(db, 'sess-1')
|
||||
insertMessage(db, 'msg-a1', 'sess-1', 1700000001000, {
|
||||
role: 'assistant', modelID: 'edenai/router-model', cost: 0,
|
||||
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
||||
})
|
||||
insertPart(db, 'part-a1', 'msg-a1', 'sess-1', {
|
||||
type: 'tool', tool: 'bash',
|
||||
state: { status: 'completed', input: { command: 'npm test' } },
|
||||
})
|
||||
})
|
||||
|
||||
const calls = await collectCalls(createOpenCodeProvider(tmpDir), dbPath, 'sess-1')
|
||||
expect(calls).toHaveLength(1)
|
||||
expect(calls[0]!.tools).toEqual(['Bash'])
|
||||
expect(calls[0]!.bashCommands).toEqual(['npm'])
|
||||
expect(calls[0]!.costUSD).toBe(0)
|
||||
})
|
||||
|
||||
it('deduplicates messages across parses', async () => {
|
||||
const dbPath = createTestDb(tmpDir)
|
||||
withTestDb(dbPath, (db) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue