fix: validate subagentTypes in cache, forge dedup key, status flag conflicts

Add subagentTypes to session-cache validateCall for consistency with
other string-array fields (tools, bashCommands, skills).

Stabilize Forge dedup key fallback: use model+tokens instead of array
index so deletions between scans don't cause double-counting.

Add mutual exclusivity validation for --day/--days/--from/--to on the
status command, matching the report command's existing checks.
This commit is contained in:
iamtoruk 2026-05-27 04:38:49 -07:00
parent 3751b3381c
commit 8d88bfd675
3 changed files with 12 additions and 3 deletions

View file

@ -462,6 +462,14 @@ program
.option('--no-optimize', 'Skip optimize findings (menubar-json only, faster)')
.action(async (opts) => {
assertFormat(opts.format, ['terminal', 'menubar-json', 'json'], 'status')
if (opts.day && (opts.from || opts.to)) {
process.stderr.write('error: --day cannot be combined with --from or --to\n')
process.exit(1)
}
if (opts.days && (opts.day || opts.from || opts.to)) {
process.stderr.write('error: --days cannot be combined with --day, --from, or --to\n')
process.exit(1)
}
await loadPricing()
const pf = opts.provider
const fp = (p: ProjectSummary[]) => filterProjectsByName(p, opts.project, opts.exclude)

View file

@ -194,13 +194,13 @@ function createParser(source: SessionSource, seenKeys: Set<string>): SessionPars
const inputTokens = Math.max(0, promptTokens - cachedInputTokens)
if (inputTokens === 0 && outputTokens === 0) continue
const model = typeof text?.model === 'string' ? text.model : 'unknown'
const calls = toolCalls(text?.tool_calls)
const { tools, bashCommands, firstCallId } = extractToolsAndCommands(calls)
const deduplicationKey = `forge:${row.conversation_id}:${firstCallId ?? i}`
const stableId = firstCallId ?? `${model}:${promptTokens}:${outputTokens}:${i}`
const deduplicationKey = `forge:${row.conversation_id}:${stableId}`
if (seenKeys.has(deduplicationKey)) continue
seenKeys.add(deduplicationKey)
const model = typeof text?.model === 'string' ? text.model : 'unknown'
yield {
provider: 'forge',
model,

View file

@ -182,6 +182,7 @@ function validateCall(c: unknown): c is CachedCall {
&& isStringArray(o['tools'])
&& isStringArray(o['bashCommands'])
&& isStringArray(o['skills'])
&& (o['subagentTypes'] === undefined || isStringArray(o['subagentTypes']))
&& isOptionalString(o['project'])
&& isOptionalString(o['projectPath'])
&& (o['toolSequence'] === undefined || (Array.isArray(o['toolSequence']) && (o['toolSequence'] as unknown[]).every(s => isToolCallArray(s))))