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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Generated by Scripts/regenerate-codex-parser-hash.sh. Do not edit by hand.

enum CodexParserHash {
static let value = "577571954a2d36bc"
static let value = "60b2fc9bcc6dd4c7"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import Foundation

extension CostUsageScanner {
enum CodexSubagentCounterSemantics: Equatable {
case independent
case copiedPrefix
}

/// Subagent source is lineage evidence, not counter semantics. The first session metadata
/// owns leaf identity; only embedded metadata proves that this rollout copied an ancestor
/// prefix. Do not restore a blanket "all subagents are independent/inherited" rule.
struct CodexSubagentRolloutShape {
let counterSemantics: CodexSubagentCounterSemantics
let ownedSuffix: CodexSubagentOwnedSuffix?
let inferredParentSessionID: String?

struct CodexSubagentOwnedSuffix {
let startLineIndex: Int
let rawTotalsBaseline: CostUsageCodexTotals
}

struct Observation {
let lineIndex: Int
let kind: Kind

enum Kind {
case sessionMetadata(id: String?)
case turnContext
case interAgentCommunication(triggerTurn: Bool)
case tokenCount(total: CostUsageCodexTotals?, last: CostUsageCodexTotals?)
}
}

static func classify(
leafSessionID: String?,
observedSessionIDs: [String?]) -> Self
{
let normalizedLeafID = Self.normalizedSessionID(leafSessionID)

let hasEmbeddedAncestor: Bool = if let normalizedLeafID {
observedSessionIDs.contains { Self.normalizedSessionID($0) != normalizedLeafID }
} else {
observedSessionIDs.count > 1 || observedSessionIDs.contains { Self.normalizedSessionID($0) != nil }
}
let distinctAncestorIDs = Set(observedSessionIDs
.compactMap(Self.normalizedSessionID)
.filter { normalizedLeafID == nil || $0 != normalizedLeafID })
let inferredParentSessionID = distinctAncestorIDs.count == 1 ? distinctAncestorIDs.first : nil

return Self(
counterSemantics: hasEmbeddedAncestor ? .copiedPrefix : .independent,
ownedSuffix: nil,
inferredParentSessionID: inferredParentSessionID)
}

static func classify(
leafSessionID: String?,
observations: [Observation]) -> Self
{
let metadataIDs = observations.reduce(into: [String?]()) { result, observation in
guard case let .sessionMetadata(id) = observation.kind else { return }
result.append(id)
}
let metadataShape = Self.classify(
leafSessionID: leafSessionID,
observedSessionIDs: metadataIDs)
guard metadataShape.counterSemantics == .copiedPrefix else { return metadataShape }

let normalizedLeafID = Self.normalizedSessionID(leafSessionID)
var lastRawTotals: CostUsageCodexTotals?
var pendingTurnContext: (lineIndex: Int, baseline: CostUsageCodexTotals)?
var ownedSuffix: CodexSubagentOwnedSuffix?
var inspectedOwnedSuffixFirstTotal = false
var observedAuthoritativeMetadata = false

for observation in observations {
switch observation.kind {
case let .sessionMetadata(id):
let normalizedID = Self.normalizedSessionID(id)
let isEmbeddedAncestor: Bool = if !observedAuthoritativeMetadata {
false
} else if let normalizedLeafID {
normalizedID != normalizedLeafID
} else {
true
}
observedAuthoritativeMetadata = true
if isEmbeddedAncestor {
// A later ancestor meta proves that any earlier candidate boundary was replay.
ownedSuffix = nil
inspectedOwnedSuffixFirstTotal = false
}
pendingTurnContext = nil

case .turnContext:
pendingTurnContext = lastRawTotals.map { (observation.lineIndex, $0) }

case let .interAgentCommunication(triggerTurn):
if ownedSuffix == nil,
triggerTurn,
let pendingTurnContext,
observation.lineIndex == pendingTurnContext.lineIndex + 1
{
ownedSuffix = Self.CodexSubagentOwnedSuffix(
startLineIndex: pendingTurnContext.lineIndex,
rawTotalsBaseline: pendingTurnContext.baseline)
inspectedOwnedSuffixFirstTotal = false
}
pendingTurnContext = nil

case let .tokenCount(total, last):
if !inspectedOwnedSuffixFirstTotal,
let suffix = ownedSuffix,
let total
{
inspectedOwnedSuffixFirstTotal = true
if let last,
Self.totalsEqual(total, last),
!Self.totalsAtLeast(total, suffix.rawTotalsBaseline)
{
// Some future protocol may copy history and then restart its counter.
// Require both a strong boundary and total==last reset evidence.
ownedSuffix = Self.CodexSubagentOwnedSuffix(
startLineIndex: suffix.startLineIndex,
rawTotalsBaseline: .init(input: 0, cached: 0, output: 0))
}
}
if let total {
lastRawTotals = total
}
pendingTurnContext = nil
}
}

return Self(
counterSemantics: .copiedPrefix,
ownedSuffix: ownedSuffix,
inferredParentSessionID: metadataShape.inferredParentSessionID)
}

static func sameConcreteSessionID(_ lhs: String?, _ rhs: String?) -> Bool {
guard let lhs = normalizedSessionID(lhs),
let rhs = normalizedSessionID(rhs)
else { return false }
return lhs == rhs
}

private static func totalsEqual(_ lhs: CostUsageCodexTotals, _ rhs: CostUsageCodexTotals) -> Bool {
lhs.input == rhs.input && lhs.cached == rhs.cached && lhs.output == rhs.output
}

private static func totalsAtLeast(_ lhs: CostUsageCodexTotals, _ rhs: CostUsageCodexTotals) -> Bool {
lhs.input >= rhs.input && lhs.cached >= rhs.cached && lhs.output >= rhs.output
}

private static func normalizedSessionID(_ value: String?) -> String? {
guard let value else { return nil }
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -907,9 +907,11 @@ extension CostUsageScanner {
}
if let parentSessionId = cached.forkedFromId {
guard let cachedDependencyKey = cached.forkBaselineDependencyKey else { return false }
let currentDependencyKey = try context.resources.inheritedResolver
.currentDependencyKey(for: parentSessionId)
guard cachedDependencyKey == currentDependencyKey else { return false }
if cachedDependencyKey != Self.codexForkDependencyNotRequiredKey {
let currentDependencyKey = try context.resources.inheritedResolver
.currentDependencyKey(for: parentSessionId)
guard cachedDependencyKey == currentDependencyKey else { return false }
}
}

if sessionAlreadyContributed {
Expand Down Expand Up @@ -974,6 +976,14 @@ extension CostUsageScanner {
if Self.cachedCodexRowsNeedIdentityRescan(cached) {
return false
}
// Subagent shape depends on the complete lineage prefix. Appended metadata can change an
// independent counter into a copied-prefix rollout, so a tail-only parse is not sound.
if try Self.codexFileIsSubagentThread(
fileURL: input.fileURL,
checkCancellation: context.checkCancellation)
{
return false
}
let startOffset = cached.parsedBytes ?? cached.size
let initialCountedTotals = cached.lastCountedTotals ?? cached.lastTotals
let initialRawTotalsBaseline = cached.lastRawTotalsBaseline ?? cached.lastTotals
Expand Down Expand Up @@ -1131,11 +1141,10 @@ extension CostUsageScanner {
range: context.range,
inheritedTotalsResolver: context.resources.inheritedResolver.inheritedTotals(for:atOrBefore:),
checkCancellation: context.checkCancellation)
let forkBaselineDependencyKey: String? = if let parentSessionId = parsed.forkedFromId {
context.resources.inheritedResolver.dependencyKeyUsed(for: parentSessionId)
} else {
nil
}
let forkBaselineDependencyKey = Self.codexForkBaselineDependencyKey(
parentSessionId: parsed.forkedFromId,
dependsOnParentTotals: parsed.dependsOnParentTotals,
inheritedResolver: context.resources.inheritedResolver)
let sessionId = parsed.sessionId ?? input.cached?.sessionId
let projectPath = parsed.projectPath ?? input.cached?.projectPath
let canonicalProjectPath = parsed.projectPath.map {
Expand Down Expand Up @@ -1237,6 +1246,19 @@ extension CostUsageScanner {
state: &state)
}

static func codexForkBaselineDependencyKey(
parentSessionId: String?,
dependsOnParentTotals: Bool,
inheritedResolver: CodexInheritedTotalsResolver) -> String?
{
guard let parentSessionId else { return nil }
guard dependsOnParentTotals else { return Self.codexForkDependencyNotRequiredKey }

// A nil key means the parent changed while its snapshots were read (or no stable
// snapshot was resolved). Preserve nil so the child cannot be reused on the next scan.
return inheritedResolver.dependencyKeyUsed(for: parentSessionId)
}

static func mergeFileDays(
existing: inout [String: [String: [Int]]],
delta: [String: [String: [Int]]])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ extension CostUsageScanner {
}
}

static func extractJSONByteBoolField(
_ field: [UInt8],
from bytes: UnsafeBufferPointer<UInt8>,
in range: Range<Int>,
atDepth targetDepth: Int) -> Bool?
{
self.extractJSONByteField(field, from: bytes, in: range, atDepth: targetDepth) { valueIndex in
self.parseJSONByteBool(in: bytes, index: &valueIndex, limit: range.upperBound)
}
}

private static func extractJSONByteField<T>(
_ field: [UInt8],
from bytes: UnsafeBufferPointer<UInt8>,
Expand Down Expand Up @@ -187,6 +198,33 @@ extension CostUsageScanner {
return sawDigit ? (sign == -1 ? -value : value) : nil
}

private static func parseJSONByteBool(
in bytes: UnsafeBufferPointer<UInt8>,
index: inout Int,
limit: Int) -> Bool?
{
if index + 4 <= limit,
bytes[index] == 0x74,
bytes[index + 1] == 0x72,
bytes[index + 2] == 0x75,
bytes[index + 3] == 0x65
{
index += 4
return true
}
if index + 5 <= limit,
bytes[index] == 0x66,
bytes[index + 1] == 0x61,
bytes[index + 2] == 0x6C,
bytes[index + 3] == 0x73,
bytes[index + 4] == 0x65
{
index += 5
return false
}
return nil
}

private static func skipJSONByteWhitespace(
in bytes: UnsafeBufferPointer<UInt8>,
index: inout Int,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import Foundation

extension CostUsageScanner {
static func extractCodexTurnContextModel(from bytes: Data) -> String? {
guard let text = truncatedUTF8String(from: bytes) else { return nil }
static func extractCodexTruncatedSessionMetadata(from bytes: Data) ->
(isSessionMetadata: Bool, sessionID: String?)
{
guard let text = truncatedUTF8String(from: bytes) else { return (false, nil) }
let object = text[...]
guard Self.extractJSONStringField("type", from: object, atDepth: 1) == "session_meta" else {
return (false, nil)
}
guard let payloadText = Self.extractJSONObjectField("payload", from: object, atDepth: 1) else {
return (true, nil)
}
let sessionID = Self.extractJSONStringField("id", from: payloadText, atDepth: 1)
?? Self.extractJSONStringField("session_id", from: payloadText, atDepth: 1)
?? Self.extractJSONStringField("sessionId", from: payloadText, atDepth: 1)
return (true, sessionID)
}

static func extractCodexTruncatedTurnContext(from bytes: Data) -> (isValid: Bool, model: String?) {
guard let text = truncatedUTF8String(from: bytes) else { return (false, nil) }
let object = text[...]
guard Self.extractJSONStringField("type", from: object, atDepth: 1) == "turn_context",
let timestamp = Self.extractJSONStringField("timestamp", from: object, atDepth: 1),
Self.dayKeyFromTimestamp(timestamp) ?? Self.dayKeyFromParsedISO(timestamp) != nil,
let payloadText = Self.extractJSONObjectField("payload", from: object, atDepth: 1)
else { return nil }
else { return (false, nil) }

let infoText = Self.extractJSONObjectField("info", from: payloadText, atDepth: 1)
let model = Self.codexTurnContextModel(
Expand All @@ -18,8 +37,8 @@ extension CostUsageScanner {
infoModelName: infoText.flatMap {
Self.extractJSONStringFieldAllowingEmpty("model_name", from: $0, atDepth: 1)
})
guard let model, model.isEmpty else { return model }
return Self.isCompleteJSONObject(payloadText) ? "" : nil
guard let model, model.isEmpty else { return (true, model) }
return (true, Self.isCompleteJSONObject(payloadText) ? "" : nil)
}

static func truncatedUTF8String(from bytes: Data) -> String? {
Expand All @@ -44,8 +63,12 @@ extension CostUsageScanner {
case "}":
depth -= 1
text.formIndex(after: &index)
if depth == 0 { return true }
if depth < 0 { return false }
if depth == 0 {
return true
}
if depth < 0 {
return false
}
case "\"":
guard Self.parseJSONString(in: text, index: &index) != nil else { return false }
default:
Expand Down
Loading