After the client returns a render tool's result, the model sometimes
re-issues the identical call with a fresh id on the follow-up run,
painting a second copy of the spending-summary card (observed live on
MAF 1.10 / CK 1.62). DeduplicateToolCallMiddleware now tracks which
render tools already ran in the current user turn and drops repeats —
their streamed TOOL_CALL_* events and their entries in the run's
MESSAGES_SNAPSHOT. The system prompt also tells the model the card is
already displayed once its "ok" result is present.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Asistente.tsx pins threadId "main" on CopilotChat (an explicit stable
id stops the per-mount message wipe), runs a hydration pass on page
load — an empty-messages run makes MAF replay the stored snapshot as
MESSAGES_SNAPSHOT with no LLM call — and sends the Inicio ask-box
question only after hydration so every normal run carries the full
client-known history. "Nueva conversación" clears the stored thread
behind a ConfirmDialog.
- server.ts: the BFF no longer injects context into empty-messages
requests (that would defeat MAF's hydration trigger), and the snapshot
reconciliation passes hydration replays through untouched and treats
ids the client already sent as history rather than duplicates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Back MAF's opt-in AG-UI snapshot persistence with a DB store so the
conversation survives page reloads and restarts:
- ChatThreadSnapshot: one upserted row per (scope, thread_id), JSONB
messages/state/interrupt (migration 794baf50635b)
- PostgresSnapshotStore implements the AGUIThreadSnapshotStore protocol.
It opens its own sessions (MAF saves during SSE streaming, after the
request middleware has closed the ContextVar session) and sanitizes on
save: camelCase toolCalls/toolCallId (the @ag-ui/client Zod schema
strips snake_case, which would lose cards on replay), drop BFF ctx-*
context messages, dedupe MAF's re-recorded multi-run turns, and drop
orphaned tool results
- Endpoint mount gains snapshot_store + a constant scope resolver
(single-user app; auth already enforced by agent_auth_and_session)
- DELETE /api/v1/chat/thread resets the conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Retiring it in the middleware audit was premature: MAF 1.10 reuses the
streamed id for plain-text snapshots but deliberately mints a fresh UUID
for post-tool-call assistant text (their #3619), so tool+text turns
still rendered a duplicate assistant bubble (re-verified live). The
user-message duplication that motivated the retirement only fired on
a2ui retry runs, which no longer exist now that a2ui is disabled.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CK 1.60+ validates generated a2ui ops against a component catalog before
painting, and our agent's ops fail validation — surfaces hang at
"Building interface" forever (MAF also drops RunAgentInput.context, so
the catalog/guidelines never even reach the model). Disable a2ui end to
end until the pipeline matures:
- server.ts: a2ui { enabled: false }; RENDER_TOOLS no longer includes
render_a2ui
- App.tsx: remove the a2ui provider prop
- agent.py: prompt now asks for clean markdown tables for lists and
structured data; render_spending_summary card guidance unchanged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- ReconcileSnapshotMiddleware removed: MAF now reuses the streamed
message id in MESSAGES_SNAPSHOT (verified in _agent_run.py at
python-1.10.0 and live event logs); keeping the surgery duplicated
user messages on a2ui follow-up runs.
- beforeRequestMiddleware now injects RunAgentInput.context as a system
message: MAF declares the field but never feeds it to the model,
which starves the model of CopilotKit's A2UI catalog/guidelines.
Remove when MAF consumes context upstream.
- Workaround chain env-gated: CK_MIDDLEWARES=off runs bare for audits,
CK_DEBUG_EVENTS=1 enables the event logger.
- Kept: SuppressRenderToolText, StripModelArtifacts,
DeduplicateToolCall, pairOrphanToolCalls (behavior-verified).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No API drift: v2 hooks, v1 useCopilotAction interop, runtime endpoint
and a2ui config all verified against the v1.62.2 source. Brings the
1.60.2 chat scroll stability fixes and 1.57.2 thread-state fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Agent/OpenAIChatCompletionClient/add_agent_framework_fastapi_endpoint
APIs verified unchanged against the python-1.10.0 source; 115 tests
green, pip check clean. MAF 1.6.0 turns OTel on by default —
ENABLE_INSTRUMENTATION=false in both compose files keeps prod logs
unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The shell revamp left SidebarInset unbounded (wrapper is min-h-svh), so
Asistente's flex chain resolved to content height — the chat's internal
scroller never engaged and long answers hid behind the composer.
Asistente now gets h-svh + overflow-hidden on the inset; every other
page keeps body scroll. Verified: inset == viewport on /asistente,
body scroll intact elsewhere.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unifies the transform pipeline on Oxc now that Rolldown does the
bundling. Dev server boot 353ms -> 188ms; build parity (2.03s).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The last JS-based stage of the toolchain (dev was already SWC+esbuild).
Local build: 18.75s/1.83GB peak RSS -> 1.97s/1.27GB, output verified
identical in the browser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The UI revamp pushed rollup's peak past the 1536MB cap and the prod
image build OOM'd. Verified locally: fails at 1536, builds at 2048.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default pointed at the compose-internal 'backend' hostname, which
never resolves when the Hono runtime runs on the host — every local
agent run died with fetch failed/ENOTFOUND before reaching FastAPI.
Prod is unaffected (sets both URLs explicitly).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
by-category and daily-spending accept start_date/end_date (range wins
over cycle); Analytics gets a two-month calendar range picker beside
the cycle selector. Fixes a latent float/Decimal TypeError that 500'd
spending_by_category whenever it had data — the category donut had
never rendered. Regression + range tests added.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Gasto del ciclo reveals the by-source breakdown, Próximas cuotas the
full active-plan list, Pensión last-period rendimientos per fund.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Header search trigger + global hotkey. Typing a question surfaces a
'Preguntar' item that lands in Asistente via the same auto-send flow
as the Inicio ask box. Also installs hover-card and calendar for the
remaining Phase 3 work.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Financiamientos shows cuota progress as Progress bars; Sincronización
uses a single Card of Item rows; Items Recurrentes table wrapped in a
Card like every other table; both window.confirm sites replaced with
the app's ConfirmDialog.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PageHeader on Budget, Proyecciones, Pensiones, Planificador,
Municipalidad, Sincronización, Financiamientos, Asistente. Pensiones
fund identity and Municipalidad meter/charge series now use the
validated chart tokens (color-mix for translucent fills). Planificador
stat cards on StatTile; deterministic chart rendering everywhere.
Municipalidad page title now matches its nav/breadcrumb label.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lib/charts.ts centralizes the color system: fixed categorical slots
(never cycled, >5 folds into Otros, 'Sin categoría' reads neutral) and
color-mix ramps that stay correct in dark mode. Animations off so
charts render deterministically.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Categories fold into 'Otros' past 5 slots (fixed hue order, never
cycled); daily spending excludes future-dated Tasa Cero cuotas; rate
chart gets a legend; PageHeader with the cycle selector as action.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- PageHeader + StatTile standardize the four header and three stat-tile
variants found in the audit; Inicio and Salarios migrated as proof.
- chart tokens replaced with a teal-anchored categorical scale, CVD-
ordered and validated (six checks, light + dark surfaces).
- Registry adds: empty, field, item, spinner, progress, toggle-group,
input-group, popover, kbd (+ toggle dep).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Próximas cuotas: headline is the upcoming billing batch (sum of every
active plan's next cuota, using the rounding-absorbing last cuota
when it's the pending one) — the in-cycle formula was ~always zero
since cuotas bill on the 19th, day one of the next cycle.
- useAgent must name the 'wealthysmart' agent (default crashed the page).
- /transactions/recent excludes future-dated Tasa Cero cuotas.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stat cards (gasto del ciclo, próximas cuotas Tasa Cero, pensión),
ask-the-assistant box that lands in Asistente and auto-sends via
agent.addMessage + runAgent, and últimas transacciones. Index route,
login redirects updated; Asistente stays at /asistente.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AppSidebar (icon-collapsible, sync warning badge, brand header),
NavUser footer (avatar + privacy/theme/logout dropdown), and a thin
header with SidebarTrigger, breadcrumbs, and quick toggles. Mobile
drawer and cmd+B come from the sidebar primitives.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
navigation.ts becomes the single source of truth for sidebar and
breadcrumbs (Inicio entry added; '/' is exact-match).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Registry-installed Base UI flavor; existing ui components untouched.
Also drops the meaningless empty registries key from components.json.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Toolbar (source tabs, search, CSV, add) and a summary strip (billing
cycle range, count, per-currency total) now live inside the table card
instead of five loose control rows. Cycle range 18-to-18 is finally
visible. Stray English strings translated; back-button spacing fixed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The clicked year/month now travel in the navigation state; Budget
seeds useBudget with them instead of always defaulting to today.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
FCL projection target defaults to the next 5-year withdrawal window
(CURRENT_AGE + 5) rather than a fixed 35.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compras minus devoluciones, one figure per currency, excluding Tasa
Cero anchors (their cuotas are what count). Tooltip explains the math.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fresh-venv installs started failing with pip ResolutionTooDeep because
requirements.txt is unpinned. Constrain CI and image builds to the
frozen, test-passing local set; requirements.txt stays the source of
intent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Violet 'Tasa Cero'/'Cuota' badges (anchor amount struck through like
deferred), convert/edit action on CC purchases with a live cuota
preview, and a Financiamientos page mirroring the bank's tab (progress,
monto cuota, saldo inicial/faltante, totals).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The anchor stays visible in listings but only its cuotas count toward
actuals — future cuotas intentionally show up in future months as
committed spend.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
BAC zero-interest financing: an anchor purchase splits into N monthly
cuota transactions (truncate-to-0.10 rule, last cuota absorbs rounding;
one cuota per 18th-cut billing cycle on the 19th). Pinned against real
Financiamientos data.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
manifest.webmanifest with the existing icons, apple-touch-icon, and SW
registration at startup. The old sw.js was a self-unregistering cleanup
stub — meaning serviceWorker.ready never resolved and web push has been
silently dead; the new worker handles push display and notification
clicks, and makes the app installable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New Planificador page: the current savings pace (prefilled from the
live budget projection's monthly net, editable) versus a scenario with
extra monthly savings, both compounded at a configurable annual rate
over a 1-40 year horizon — side-by-side totals and growth curves.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Analytics gains a 90-day USD/CRC buy/sell chart from the existing
history endpoint (wishlist: rate history). Pensiones gains a
Calculadora de Aporte: fund + target amount + target age → required
monthly contribution via the annuity future-value formula, using the
fund's live balance and configured rate (wishlist: contribution
calculator).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
One chevron component now drives year/month stepping in Budget and
Proyecciones (UX-03). Jumping from Proyecciones to a Budget month shows
a back affordance (UX-22). Water meters can be named (Casa, Cochera…)
via a settings-persisted dialog; the chart legend uses the names
(UX-24).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every dict-returning route the SPA consumes now declares a response
model (auth, bulk, sync-status, notifications). api-types.gen.ts is
generated from app.openapi() via openapi-typescript, and the 22 hand-
written read interfaces in api.ts are now aliases onto the generated
schemas — backend schema drift becomes a typecheck failure, and CI
regenerates the file and fails the deploy if it's stale. The aliasing
immediately caught two real drifts: RecurringItemType was missing
SAVINGS, and raw_charges (untyped JSON column) is now explicitly
shaped at the boundary.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per-fund contribution and annual-rate assumptions are now editable in a
Supuestos dialog on Pensiones and persisted server-side in UserSettings
so projections survive devices and reloads; saldo inicial keeps coming
from the latest snapshot (UX-12). The recurring-item dialog confirms
before discarding unsaved edits, with the dirty baseline computed from
the same values the populate effect sets (UX-06). Empty transaction
lists now offer an inline add button (UX-14).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Transactions gain a checkbox column with select-all; a bulk bar offers
assign-category, defer/restore and delete (confirmed) via the new bulk
endpoint, with toasts and invalidation. The paste-import modal now
lists each skipped duplicate with the matched existing row and offers
'Importar duplicados de todos modos' — re-importing ONLY the skipped
lines so first-pass rows can't double-import (UX-20). Two real fixes
found en route: the mobile list ignored undo-pending deletes and used
en-US dates; the desktop date column also lost its en-US stray. Note:
UX-11 (no mobile layout) was a false positive — a card layout already
existed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Paste-import now returns each skipped duplicate WITH the matched
existing row (id, merchant, date, amount, source) instead of a bare
count, and accepts allow_duplicates to force-import false positives —
the practical fix for same-day identical purchases hashing together
(UX-20; supersedes the BE-08 hash change, which would have orphaned
every existing hash). POST /transactions/bulk applies
delete/set_category/set_deferred to up to 500 ids atomically. 82 tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fund visibility toggles and the header privacy/theme buttons expose
aria-pressed (FE-16); deferred transactions render struck-through and
muted so the state is visible at a glance (UX-05); the balance-override
input documents its keyboard contract (UX-21). The Budget CSV button
rides in the transactions tab header.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>