diff --git a/src/core/budget/budget-tracker.ts b/src/core/budget/budget-tracker.ts index d5bf44e21..7404b4279 100644 --- a/src/core/budget/budget-tracker.ts +++ b/src/core/budget/budget-tracker.ts @@ -32,6 +32,7 @@ import { mkdirSync, appendFileSync } from 'node:fs'; import { dirname } from 'node:path'; import { gbrainPath } from '../config.ts'; import { ANTHROPIC_PRICING, type ModelPricing } from '../anthropic-pricing.ts'; +import { canonicalLookup } from '../model-pricing.ts'; import { EMBEDDING_PRICING, lookupEmbeddingPrice } from '../embedding-pricing.ts'; import { splitProviderModelId } from '../model-id.ts'; import { isoWeekFilename, resolveAuditDir } from '../audit-week-file.ts'; @@ -201,6 +202,12 @@ function lookupPricing(modelId: string, kind: BudgetKind): ModelPricing | null { if (kind === 'rerank' && providerId && FREE_LOCAL_RERANK_PROVIDERS.has(providerId)) { return { input: 0, output: 0 }; } + // Fall back to the full canonical pricing table so non-Anthropic chat + // models with a known price (openai:*, google:*, deepseek:*) resolve under + // --max-cost instead of TX2 no_pricing hard-failing at $0. ANTHROPIC_PRICING + // above is only the bare-keyed Claude view. + const canon = canonicalLookup(modelId); + if (canon) return canon; return null; } diff --git a/src/core/pglite-engine.ts b/src/core/pglite-engine.ts index f74e4e964..a8204a8aa 100644 --- a/src/core/pglite-engine.ts +++ b/src/core/pglite-engine.ts @@ -5962,6 +5962,9 @@ export class PGLiteEngine implements BrainEngine { ); } + // Exclude dream/synthesize-generated pages (parity with postgres-engine). + where.push(`(p.frontmatter ->> 'dream_generated') IS DISTINCT FROM 'true'`); + const orderKey = ENRICH_ORDER_SQL[opts.order] ? opts.order : 'inbound-links'; const orderBy = ENRICH_ORDER_SQL[orderKey]; diff --git a/src/core/postgres-engine.ts b/src/core/postgres-engine.ts index 6deaad784..7a9cfdde3 100644 --- a/src/core/postgres-engine.ts +++ b/src/core/postgres-engine.ts @@ -6255,6 +6255,12 @@ export class PostgresEngine implements BrainEngine { )` : sql``; + // Exclude dream/synthesize-generated pages (reflections, originals, cycle + // logs carrying frontmatter dream_generated:true). enrich develops ENTITY + // stubs; running it on a generated essay/log creates circular self-citation + // and drops the H1. IS DISTINCT FROM 'true' keeps NULL/'false' rows. + const dreamCondition = sql`AND (p.frontmatter ->> 'dream_generated') IS DISTINCT FROM 'true'`; + // Whitelisted ORDER BY (no injection — enum maps to a literal fragment). const orderKey = ENRICH_ORDER_SQL[opts.order] ? opts.order : 'inbound-links'; const orderBy = sql.unsafe(ENRICH_ORDER_SQL[orderKey]); @@ -6278,6 +6284,7 @@ export class PostgresEngine implements BrainEngine { AND (char_length(p.compiled_truth) + char_length(COALESCE(p.timeline, ''))) < ${threshold} ${sourceCondition} ${recencyCondition} + ${dreamCondition} ORDER BY ${orderBy} LIMIT ${limit} `; diff --git a/src/core/write-through.ts b/src/core/write-through.ts index 02f792a3f..dc9510e85 100644 --- a/src/core/write-through.ts +++ b/src/core/write-through.ts @@ -146,7 +146,12 @@ export async function writePageThrough( frontmatterOverrides: opts.frontmatterOverrides, }); - mkdirSync(dirname(filePath), { recursive: true }); + // On Bun + Windows, mkdirSync(dir, { recursive: true }) can still throw + // EEXIST when the directory already exists (POSIX no-ops it). That aborts + // the put_page / enrich / capture write-through whenever the prefix dir + // already exists, silently leaving the DB and the .md file plane out of sync. + const targetDir = dirname(filePath); + if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true }); // Atomic write: unique temp sibling + rename. Unique name (pid + random) // so two concurrent saves to the same target can't clobber each other's