diff --git a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift index ca353e24bb..6bcb4da8ac 100644 --- a/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift +++ b/Sources/CodexBarCore/Generated/CodexParserHash.generated.swift @@ -1,5 +1,5 @@ // Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand. enum CodexParserHash { - static let value = "3aa49b47f4b78e13" + static let value = "f90ee3aa5b4f84a2" } diff --git a/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner+CodexPriority.swift b/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner+CodexPriority.swift index af24e5e88c..dbe0230473 100644 --- a/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner+CodexPriority.swift +++ b/Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner+CodexPriority.swift @@ -258,7 +258,8 @@ extension CostUsageScanner { from logs where ts >= ? and ts < ? and (feedback_log_body like '%websocket request:%' - or feedback_log_body like '%response.completed%') + or feedback_log_body like '%response.completed%' + or feedback_log_body like '%service_tier: Some(Some("priority"))%') """ var stmt: OpaquePointer? guard sqlite3_prepare_v2(db, query, -1, &stmt, nil) == SQLITE_OK else { return [:] } @@ -507,7 +508,8 @@ extension CostUsageScanner { from logs indexed by idx_logs_ts where ts >= ? and (feedback_log_body like '%websocket request:%' - or feedback_log_body like '%response.completed%') + or feedback_log_body like '%response.completed%' + or feedback_log_body like '%service_tier: Some(Some("priority"))%') order by rowid """, true) @@ -518,7 +520,8 @@ extension CostUsageScanner { from logs where rowid > ? and ts >= ? and (feedback_log_body like '%websocket request:%' - or feedback_log_body like '%response.completed%') + or feedback_log_body like '%response.completed%' + or feedback_log_body like '%service_tier: Some(Some("priority"))%') order by rowid """, false) @@ -539,7 +542,9 @@ extension CostUsageScanner { #endif static func parseCodexPriorityTraceRow(timestamp: String?, body: String) -> CodexPriorityTurnMetadata? { - guard let markerRange = body.range(of: self.requestMarker) else { return nil } + guard let markerRange = body.range(of: self.requestMarker) else { + return self.parseCodexPrioritySubmissionRow(timestamp: timestamp, body: body) + } let prefix = String(body[.. CodexPriorityTurnMetadata? + { + guard body.contains(#"service_tier: Some(Some("priority"))"#), + let submissionRange = body.range(of: "Submission sub=Submission {") + else { return nil } + let submission = String(body[submissionRange.upperBound...]) + guard let turnID = self.quotedValue(named: "id", in: submission) else { return nil } + + return CodexPriorityTurnMetadata( + threadID: self.value( + named: "thread_id", + in: String(body[.. (turnID: String, model: String)? { let marker = "websocket event:" guard let markerRange = body.range(of: marker) else { return nil } @@ -584,11 +608,19 @@ extension CostUsageScanner { guard let range = text.range(of: "\(name)=") else { return nil } let tail = text[range.upperBound...] let value = tail.prefix { char in - !char.isWhitespace && char != "," && char != "]" && char != ")" + !char.isWhitespace && char != "," && char != "]" && char != ")" && char != "}" && char != ":" } return value.isEmpty ? nil : String(value) } + private static func quotedValue(named name: String, in text: String) -> String? { + guard let range = text.range(of: "\(name): \"") else { return nil } + let tail = text[range.upperBound...] + guard let end = tail.firstIndex(of: "\"") else { return nil } + let value = tail[.. String? { guard sqlite3_column_type(stmt, index) != SQLITE_NULL, diff --git a/Tests/CodexBarTests/CostUsageScannerCodexPriorityTests.swift b/Tests/CodexBarTests/CostUsageScannerCodexPriorityTests.swift index 65f8698850..3124e1e79d 100644 --- a/Tests/CodexBarTests/CostUsageScannerCodexPriorityTests.swift +++ b/Tests/CodexBarTests/CostUsageScannerCodexPriorityTests.swift @@ -20,6 +20,22 @@ struct CostUsageScannerCodexPriorityTests { #expect(parsed?.timestamp == "2026-05-10T12:00:00Z") } + @Test + func `parses current priority submission metadata without exposing prompt`() { + let body = "session_loop{thread_id=thread}: Submission sub=Submission { " + + "id: \"turn\", op: UserInput { text: \"private\" }, " + + #"thread_settings: ThreadSettingsOverrides { service_tier: Some(Some("priority")) }"# + + let parsed = CostUsageScanner.parseCodexPriorityTraceRow( + timestamp: "1785434553", + body: body) + + #expect(parsed?.threadID == "thread") + #expect(parsed?.turnID == "turn") + #expect(parsed?.model == nil) + #expect(parsed?.timestamp == "1785434553") + } + @Test func `ignores non priority malformed and non response request rows`() { let prefix = "thread_id=thread turn.id=turn websocket request: " @@ -74,6 +90,26 @@ struct CostUsageScannerCodexPriorityTests { #expect(turns["turn-a"]?.model == "request-model") } + @Test + func `reads current priority submission rows from sqlite logs table`() throws { + let env = try CostUsageTestEnvironment() + defer { env.cleanup() } + let dbURL = env.root.appendingPathComponent("logs_2.sqlite") + try Self.createTestLogsDatabase(at: dbURL) + try Self.insertTestLog( + dbURL: dbURL, + timestamp: "2026-05-10T12:00:00Z", + body: "session_loop{thread_id=thread}: Submission sub=Submission { " + + "id: \"turn\", op: UserInput { text: \"private\" }, " + + #"thread_settings: ThreadSettingsOverrides { service_tier: Some(Some("priority")) }"#) + + let turns = CostUsageScanner.codexPriorityTurns(databaseURL: dbURL) + + #expect(turns.keys.sorted() == ["turn"]) + #expect(turns["turn"]?.threadID == "thread") + #expect(turns["turn"]?.model == nil) + } + @Test func `cold scan uses timestamp index and warm scan uses rowid cursor`() throws { let env = try CostUsageTestEnvironment()