* feat(dashboard): masonry packing + per-widget size controls
In two-column mode the dashboard used a row-based CSS grid, so cards
stretched to equal row height and left dead space (e.g. the Net Worth
chart padded out to match the tall Balance Sheet table). Replace the
row-based layout with masonry packing and add per-widget size guardrails.
- Masonry: CSS grid with computed row-spans + grid-auto-flow: dense, driven
by a dashboard-masonry Stimulus controller (ResizeObserver + turbo:frame-load).
The DOM stays a single flat list, so drag/keyboard reorder is unaffected.
Active only in multi-column mode; single column falls back to normal flow.
- Internal sizing: the net worth chart height is now driven by a
--dash-widget-h CSS var (fixes an inert flex-1) so the card no longer pads
out below the chart.
- Guardrails: per-widget layout metadata (col_span, grow, min_height,
width_toggle) in PagesController, with per-user overrides persisted under
preferences["dashboard_section_layout"], deep-merged so width and height coexist.
- Size menu: a hover control on size-capable cards — Width (Half/Full) for the
cashflow sankey and net worth chart, Height (Compact/Auto/Tall) for grow
widgets. The sankey defaults to full width.
Adds model + controller tests for preference persistence and i18n keys.
* refactor(dashboard): redesign size menu with segmented controls
The size menu used a plain radio list and a diagonal maximize-2 trigger that
collided with the cashflow sankey's modal-expand button. Replace it with a
layout-config popover: a sliders-horizontal trigger plus two labeled axis
groups (Width, Height), each rendered as a DS::SegmentedControl with the
active option filled. Clearer, more compact, and it reads as a card-layout
control. The widget-size controller now mirrors the segmented control's
active-class + aria-pressed contract.
* feat(dashboard): expose width toggle on balance sheet and investments
Tables benefit from horizontal room, so give Balance Sheet and Investments
the same Width (Half/Full) control as the sankey and net worth chart. Height
presets stay chart-only — a table sized to a fixed height would just add
whitespace or force scrolling. The Outflows donut is intentionally left out
(full width is mostly whitespace for a donut).
* fix(dashboard): address review feedback on size controls
- Only apply the full-width col-span and show the Width control when the
two-column layout is enabled. A full widget previously leaked
2xl:col-span-2 into the single-column grid, creating an implicit second
column at 2xl widths and breaking the single-column preference. This also
keeps the Width control coherent with the Appearance two-column setting
(it now appears only where it does something). [Codex]
- Stop size-menu keydowns from bubbling to the section reorder handler, so
keyboard users can open the menu and pick options without entering
grab/reorder mode. [Codex]
- Harden preferences params: ignore a malformed (non-hash)
dashboard_section_layout / collapsed_sections instead of raising a 500,
and require section_order to be an array. [CodeRabbit]
- Localize the dashboard sections aria-label. [CodeRabbit]
* chore(settings): mention per-widget size controls in two-column copy
Surface the new per-widget width/height controls in the Appearance
"Two-column layout" description so the capability is discoverable.
* test(dashboard): assert non-mutation for malformed layout input
Addresses review feedback: asserting assert_nil made the test depend on
the fixture happening to have no dashboard height for net_worth_chart.
Capture the pre-PATCH value and assert it is unchanged, so the test
stays valid (malformed input ignored) even if the fixture later gets a
default height.
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Guillem Arias <guillem.arias@col.vueling.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* First pass of Akahu
* fix up sync all
* conflicts
* fix db migration issue? - fix auto selection of akahu account type
* Address Akahu PR feedback
* Complete provider metadata
* Fix PR 1921 CI tests
* PR feedback
* PR feedback
* post merge
---------
Co-authored-by: failing <failing@users.noreply.github.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
Co-authored-by: sure-admin <sure-admin@splashblot.com>
* feat(ai): add Anthropic provider with chat parity (1/5)
Introduces Provider::Anthropic alongside Provider::Openai, implementing
the LlmConcept chat_response contract over the official anthropic Ruby
SDK. Batch ops, PDF, and RAG land in follow-up PRs.
- Provider::Anthropic uses Messages API for sync and streaming responses
- ChatConfig builds requests with ephemeral prompt-cache markers on the
system prompt and the last tool definition
- MessageFormatter reconstructs multi-turn history (text + tool_use +
tool_result blocks) from raw Message records, including the paired
user-role tool_result turn Anthropic requires after every tool_use
- ChatParser maps Anthropic Message into the shared ChatResponse Data
- Registry, Setting, User, Chat default model wired for ANTHROPIC_*
envs and Setting.anthropic_*; LLM_PROVIDER selects between providers
- Responder forwards raw conversation_history (Array<Message>) so
providers without hosted conversation state can rebuild context
- OpenAI provider accepts and ignores the new kwarg (no behavior change)
Tests cover provider init, model gating, MessageFormatter for all turn
shapes, ChatConfig request building (max_tokens, system cache, tool
conversion), ChatParser for text / tool_use / mixed blocks, Registry
discovery, and mocked chat_response success / error / function_request
paths. Live VCR cassettes recorded in a follow-up with a real key.
Stacked PRs: 2/5 batch ops + cost ledger, 3/5 PDF, 4/5 pgvector RAG,
5/5 settings UI + disclosure.
* fix(ai): address PR review on Anthropic provider foundation
Surface fixes raised by Codex + CodeRabbit on PR 1/5:
- Provider::Anthropic#chat_response now accepts (and ignores) a
`messages:` kwarg. Assistant::Responder passes both `messages:`
(OpenAI-shape) and `conversation_history:` (raw Message records) for
cross-provider parity, so the previous signature raised
ArgumentError on the first chat turn through the Anthropic provider.
- Provider::Anthropic#supports_model? bypasses the `claude` prefix
gate when a custom base_url is configured, mirroring the OpenAI
provider. Bedrock-shaped IDs like
`anthropic.claude-sonnet-4-5-20250929-v1:0` and
`claude-opus-4@20250514` are otherwise rejected by
Assistant::Provided#get_model_provider and the chat dies.
- Setting.anthropic_access_token is now in
EncryptedSettingFields::ENCRYPTED_FIELDS so the Anthropic API key
is encrypted at rest like every other provider secret. Previously
plaintext while siblings (openai_access_token, twelve_data_api_key,
external_assistant_token) were ciphertext.
- Chat.default_model falls back to whichever provider is actually
configured. Previously, with LLM_PROVIDER=anthropic but no
Anthropic credentials, the default model resolved to a Claude ID
that no registered provider supported, so chats failed even when
OpenAI was fully configured. Adds Provider::{Anthropic,Openai}#configured?
class methods for the readable callsite.
- Provider::Anthropic.effective_model uses
`ENV["ANTHROPIC_MODEL"].presence || Setting.anthropic_model` so the
Setting lookup is only performed when the env var is absent — the
previous `ENV.fetch(KEY, default)` evaluated the default arg
eagerly on every call.
- Provider::Anthropic::ChatConfig#anthropic_input_schema strips both
`:strict` and `"strict"` keys so JSON-decoded schemas with string
keys cannot leak the OpenAI-only flag through to Anthropic.
Test coverage added: supports_model? bypass on custom endpoints,
chat_response messages: kwarg compatibility, default_model fallback
in the three credential combinations, configured? against ENV +
Setting, strict-flag stripping for both key types, and a
`Setting.expects(:anthropic_model).never` assertion proving the
ENV-precedence test now exercises the lazy path.
All 4365 tests pass (1 pre-existing libvips env error unrelated).
* test(chat): make default_model tests resilient to ENV model overrides
CodeRabbit flagged on PR review: the new default_model tests asserted
against Provider::*::DEFAULT_MODEL, but Chat.default_model actually
returns Provider::*.effective_model.presence (which reads
OPENAI_MODEL / ANTHROPIC_MODEL from the environment). With either env
var set, the tests would fail intermittently even though routing was
correct.
- New default_model tests now assert against the provider's
effective_model directly, so they verify the routing decision
(which provider's value wins) without coupling to the constant.
- Pre-existing "creates with default model" assertions had the same
brittleness; switch them to compare against Chat.default_model so
the chosen model is whatever the env / Setting cascade resolves to.
Verified by running `ANTHROPIC_MODEL=claude-haiku-4-5 OPENAI_MODEL=gpt-4o
bin/rails test test/models/chat_test.rb` — 16 runs, 0 failures
(previously 2 pre-existing failures + 0 from the new tests).
* fix(ai): address local review on Anthropic foundation
- Provider::Anthropic#supports_pdf_processing? bypasses prefix gate for
custom endpoints, mirroring supports_model?
- Provider::Anthropic#initialize raises Error when custom_endpoint? AND
model.blank?, parity with Provider::Openai
- stream_chat_response captures partial usage on mid-stream errors and
records it via the new on_partial callback so chat_response can skip
the duplicate error row in the outer rescue
- safe_accumulated_message swallows the secondary failure when the SDK
cannot reconstruct a snapshot
- langfuse_client memoizes properly (||= instead of =) so repeated calls
don't churn Langfuse instances
- MessageFormatter sorts tool_calls by created_at then id so the
message array is deterministic across replays; skips tool_calls
missing both provider_call_id and provider_id rather than sending
`id: nil` and getting rejected by Anthropic
- Setting.anthropic_access_token default falls back through
ENV["ANTHROPIC_API_KEY"].presence (was missing .presence, so an
empty-string env value bled through)
- User#openai_configured? / #anthropic_configured? delegate to the
Provider::* class methods — single source of truth
- Assistant::Responder renames the OpenAI-shape history builder
conversation_history → openai_messages_payload so the kwarg name
matches the local method name (messages: openai_messages_payload,
conversation_history: chat_message_records)
- Assistant::Builtin stale-history comment updated to reference both
builders
Adds a streaming chat_response test using ad-hoc subclasses of the
SDK event types so the case/when dispatch matches via is_a? without
stubbing class-level === behavior.
* test(ai): add Anthropic tool_use round-trip + multi-tool turn coverage
Addresses @jjmata's "worth confirming" note on PR #1983: tool-use turns
from prior assistant messages must round-trip correctly when retrieved
from the database.
- New `ChatParser → ToolCall::Function → MessageFormatter` test walks
the full path: Anthropic response with a tool_use block →
ChatFunctionRequest → ToolCall::Function.from_function_request →
persisted on the AssistantMessage → MessageFormatter rebuild on the
next turn. Asserts the original `tool_use.id` is preserved end-to-end
as both `tool_use.id` and the paired `tool_result.tool_use_id`, and
that the original `input` hash and serialized result content survive.
- New multi-tool assistant turn test confirms two tool_use blocks on a
single assistant message render as two tool_use blocks followed by
two paired tool_result blocks in a single user-role follow-up,
matching Anthropic's required alternation.
Both tests exercise the existing PR1 code without behavior changes.
* test(ai): require "ostruct" explicitly in Anthropic provider tests
OpenStruct is moving out of Ruby's default load path (warning in 3.4+,
removed in 3.5+). Tests work today because ActiveSupport transitively
loads it, but that's incidental. Match the existing convention in
test/controllers/settings/hostings_controller_test.rb which explicitly
requires ostruct for the same reason.
* fix(ai): sanitize Langfuse warn logs, normalize tool_use.input, dedup history fetch
Addresses three open CodeRabbit findings on PR #1983.
- Provider::Anthropic Langfuse rescue branches no longer include
`e.full_message` in `Rails.logger.warn`. `full_message` bundles the
backtrace + cause chain and on some SDK error types includes the
serialized request/response payload (prompt, model output). Logs
now report `#{e.class}: #{e.message}` only. Three sites:
create_langfuse_trace, log_langfuse_generation, upsert_langfuse_trace.
Note: Provider::Openai has the same pattern (copy-pasted source) —
harmonization deferred to a follow-up cleanup PR; this commit fixes
only the Anthropic provider to keep PR scope tight.
- MessageFormatter#parse_arguments now coerces any non-Hash parsed
result to `{}`. Anthropic's Messages API requires `tool_use.input`
to be a JSON object (map); a stored ToolCall::Function record whose
arguments parse to a scalar, bool, or array (corrupt row, legacy
data, cross-provider bleed) would otherwise produce a payload the
API rejects. Normal flow stores Hash arguments end-to-end so the
fix is defensive — adds 2 tests covering scalar/array JSON strings
and non-String non-Hash inputs.
- Assistant::Responder dedups the chat-history fetch. The previous
layout fired two near-identical `chat.messages.where(...).includes(
:tool_calls).ordered` queries per LLM turn (one for the OpenAI-shape
payload, one for the raw-records kwarg). A new memoized
`complete_chat_messages` fetches once; `chat_message_records` filters
out the current message via `Array#reject`, `openai_messages_payload`
iterates the cached array unchanged. One SQL query per turn instead
of two. Memoization scope = single Responder instance (per LLM call),
so cache invalidation is not a concern.
All 4370 tests pass (1 pre-existing libvips env error unrelated).
Rubocop + brakeman clean.
* fix(ci): replace sk-ant- prefixed test placeholders
Pipelock secret scanner pattern-matches `sk-ant-*` as a real Anthropic
API key and fails the PR security-scan check. Test stubs and
ClimateControl env values used `sk-ant-test`, `sk-ant-from-setting`,
`sk-ant-x`, `sk-ant-y` as obvious placeholders, but the scanner does
not care about value entropy.
Switched to `fake-anthropic-key-*` / `fake-token-*` strings so the
scanner stops flagging them. No production code touched, no behavior
change — Provider::Anthropic still accepts any non-blank token.
* refactor: rename beta features gate to preview features
Renames the opt-in gate introduced in PR #1829 from "beta" to "preview".
Same shape (per-user JSONB toggle, `before_action` concern, marker pill)
just retitled so the surface speaks the language Sure uses elsewhere
("preview" reads as in-progress, "beta" had baggage with provider
maturity copy and external testing programs).
Renames:
- BetaGateable -> PreviewGateable
- require_beta_features! -> require_preview_features!
- beta_features_enabled? -> preview_features_enabled?
- preferences["beta_features_enabled"] -> preferences["preview_features_enabled"]
- DS::Pill default label "Beta" -> "Preview"
- Settings -> Preferences toggle copy "beta features" -> "preview features"
- config/locales/views/beta/ -> config/locales/views/preview/
- docs/llm-guides/gating-a-beta-feature.md -> gating-a-preview-feature.md
Includes a data migration that copies any existing
`beta_features_enabled` JSONB key into `preview_features_enabled` so early
opt-ins survive the rename, then removes the old key. The migration is
fully reversible.
Provider maturity copy ("maturity.beta = Beta" under Settings -> Bank
sync) is intentionally untouched - that's a separate concept describing
a provider's integration stability, not Sure's feature gate.
* review: apply CodeRabbit findings on PR #1837
- Settings::PreferencesController#update now routes the
`preview_features_enabled` input through strong params and casts via
ActiveModel::Type::Boolean instead of reading raw params and string-
comparing to "1". Matches Sure's controller convention for permitted
params and avoids stringly-typed boolean handling.
- Rename migration now wraps the destination JSONB key write in COALESCE
so a row that somehow ends up with both keys keeps the destination
value instead of having it overwritten by the source. Up and down
paths get the same defensive shape.
* 📝 CodeRabbit Chat: Implement requested code changes
* 📝 CodeRabbit Chat: Implement requested code changes
* fix: restore all missing translation keys; rename beta→preview label
* fix: restore all missing sections (appearances, debugs, llm_usages, providers, etc.); rename beta→preview
* fix: restore missing keys (member_removal_failed, confirm_delete, etc.); add preview section
* fix(i18n/ca): use 'està en vista prèvia' instead of 'és una vista prèvia'
* fix(i18n/ca): use 'en desenvolupament'; drop article in preview title
* fix(i18n/es): use 'en desarrollo' instead of 'en progreso'
* fix(i18n/ca): use 'funcions experimentals' instead of 'vista prèvia'
* fix(i18n/es): use 'funciones experimentales' instead of 'vista previa'
* fix(i18n/ca): use 'funcions experimentals' in preferences.show.preview
* fix(i18n/es): use 'funciones experimentales' in preferences.show.preview
* fix(i18n/ca): use 'Experimental' pill label instead of 'Vista prèvia'
* fix(i18n/es): use 'Experimental' pill label instead of 'Vista previa'
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* feat: beta features toggle + Beta pill primitive
Adds the infrastructure for self-service beta opt-in. No call sites yet:
this PR is meant to land first so feature PRs (Goals, etc.) can ship
behind the gate incrementally.
User opts in via a single toggle at the bottom of Settings → Preferences.
The flag persists in the existing `users.preferences` JSONB column under
`beta_features_enabled` — same shape as `dashboard_two_column` and
`show_split_grouped`, so no migration is needed.
Controllers gate a beta feature by adding `before_action
:require_beta_features!` from the new `BetaGateable` concern (included in
ApplicationController). Views use the `beta_features_enabled?` helper to
hide / show nav items, banners, etc. Logged-out callers always return
false.
Ships `DS::BetaPill`, a small inline marker for tagging features as
Beta / Canary in nav, headers, and lists. Five tones (violet by default,
indigo, fuchsia, amber, gray) map to existing Sure color tokens — no raw
hex. Three styles (soft / filled / outline) and two sizes (sm / md) cover
the surfaces in the design handoff. The `dot_only:` mode renders just
the colored dot for use on a collapsed sidebar.
* review: rename to DS::Pill, fix CR/Codex nits, add tests
CodeRabbit + Codex review feedback:
- Rename DS::BetaPill → DS::Pill. The component was already generic in
shape (tones, styles, sizes); the name was misleading scope. "Beta"
becomes the default label (still i18n-driven). Goals' StatusPill can
later refactor onto this primitive without a third pill.
- Localize the default pill label via i18n (`ds.pill.default_label`)
instead of hard-coding English.
- Add role="img" to the dot-only span so the aria-label is consistently
exposed to assistive tech.
- Wrap the Preferences toggle row in <label for="…"> so the title and
description become an honest click target for the toggle (matches the
cursor-pointer affordance).
- Drop arbitrary Tailwind values (py-[3px], gap-[5px], tracking-[…]) in
favor of scale tokens. text-[10/11px] stays because the pill is
intentionally sub-12px (Sure's smallest scale token is text-xs / 12px)
to read as a marker, not a label.
- Add User#beta_features_enabled? predicate tests covering default-off,
explicit-true, and non-boolean truthy values.
Won't fix:
- Palette refs (`--color-violet-*` etc.). Sure has no semantic Beta/
Canary tokens; introducing them in this PR would be a design-system
change beyond the scope. The component centralizes palette use in one
`palette` method, matching the existing pattern in
Goals::StatusPillComponent.
* review: consistent title fallback in full-pill branch
* docs: how to gate a feature behind the beta toggle
* docs: unwrap doc lines to match existing style
* chore(preview): run Cloudflare PR previews on basic instances (#1831)
* fix(preview): use Rails health endpoint for container ping (#1823)
* fix(preview): use Rails health endpoint for container ping
* fix(preview): point container ping to localhost/up
---------
Co-authored-by: Sure Admin (bot) <sure-admin@splashblot.com>
* Fix missing translation fallback for account subtypes in LunchFlow and Mercury
The translate_subtypes lambda called t() without a default: fallback, causing
"Translation missing" for the ~39 investment subtypes not in the locale files.
Now falls back to the :long label from the model's SUBTYPES hash (e.g. "457(b)",
"SEP IRA", "UGMA Custodial Account"), matching the pattern used by Accountable#subtype_label_for.
https://claude.ai/code/session_01HBJzNkvpky8mKrLkzv83Mm
* Default to single-column dashboard
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Initial split transaction support
* Add support to unsplit and edit split
* Update show.html.erb
* FIX address reviews
* Improve UX
* Update show.html.erb
* Reviews
* Update edit.html.erb
* Add parent category to dialog
* Update en.yml
* Add UI indication to totals
* FIX ui update
* Add category select like rest of app
* Add split ui
* Add settings configuration for split transactions
- Adds a new settings section for appearance changes
- Also adds extra checks for delete and API calls
- Also adds checks for parent/child changes
* fixes
- split transactions dark mode fix
- add split transactions to context menu
* Update entry.rb
1. New validation split_child_date_matches_parent — prevents saving a split child with a date different from its parent. This is the root-cause fix that
protects all flows at once.
2. Bulk update guard — bulk_update! now strips :date from attributes when processing split children, preventing the validation from raising and silently
skipping the date change instead.
* N+1 fix for split_parent?
* Update entry.rb
Problem: In bulk_update!, when a split child has :date removed from attrs (line 432) and the remaining attrs is empty (e.g., the bulk update only
changed the date), entry.update! {} still ran as a no-op. But lock_saved_attributes! and mark_user_modified! at lines 443-444 executed unconditionally,
incorrectly marking untouched split children as user-modified and opting them out of future syncs.
Fix:
1. Added a changed flag to track whether any actual modification happened
2. Wrapped entry.update! in an if attrs.present? check so no-op updates are skipped
3. Gated lock_saved_attributes! and mark_user_modified! behind if changed, so they only run when the entry was actually modified (either via attribute
update or tag update)
* fixes
1. Indentation in show.html.erb Settings section — The split button block and delete block had extra indentation making them appear nested inside guard
blocks they weren't part of. Fixed to match actual nesting.
2. Skip @split_parents query when grouping is off — The controller now only loads split parent entries when show_split_grouped? is true, saving a query
with joins when the feature is disabled.
* feat: Add default account for manual transaction entries (#1061)
Allow users to designate a default account that auto-selects
in the transaction creation form. Also consolidates account list
actions (edit, link/unlink, enable/disable) into a meatball menu.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* - handle context menu width on mobile
- restrict default account to depository types only
- added FR, ES and DE i18n files
* - Add credit card accounts can also be used as default
- Moved logic into controller
* Scope context menu max-width to accounts menu only
- decouples the width constraint from the shared DS::Menu component by introducing an optional max_width param
* fix ci test and address issues raised by coderabbit and codex
* Address CodeRabbit review feedback
- Use .present? for institution_name guards to avoid empty UI artifacts
- Align "Set default" menu visibility with actual preselection eligibility
(active + unlinked + supports_default?) to prevent drift between UI and model
- Keep disabled star visible when account is already default but now ineligible
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add eligible_for_transaction_default? predicate to Account model
Consolidates active + unlinked + supports_default? checks into a single
shared predicate used by the controller, view, and user model guard,
preventing a direct PATCH from bypassing UI eligibility rules.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Added "Unset default" option
Added negative test for default account
Removed duplicated logic for account.eligible_for_transaction_default
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(helm): add Pipelock ConfigMap, scanning config, and consolidate compose
- Add ConfigMap template rendering DLP, response scanning, MCP input/tool
scanning, and forward proxy settings from values
- Mount ConfigMap as /etc/pipelock/pipelock.yaml volume in deployment
- Add checksum/config annotation for automatic pod restart on config change
- Gate HTTPS_PROXY/HTTP_PROXY env injection on forwardProxy.enabled (skip
in MCP-only mode)
- Use hasKey for all boolean values to prevent Helm default swallowing false
- Single source of truth for ports (forwardProxy.port/mcpProxy.port)
- Pipelock-specific imagePullSecrets with fallback to app secrets
- Merge standalone compose.example.pipelock.yml into compose.example.ai.yml
- Add pipelock.example.yaml for Docker Compose users
- Add exclude-paths to CI workflow for locale file false positives
* Add external assistant support (OpenAI-compatible SSE proxy)
Allow self-hosted instances to delegate chat to an external AI agent
via an OpenAI-compatible streaming endpoint. Configurable per-family
through Settings UI or ASSISTANT_TYPE env override.
- Assistant::External::Client: SSE streaming HTTP client (no new gems)
- Settings UI with type selector, env lock indicator, config status
- Helm chart and Docker Compose env var support
- 45 tests covering client, config, routing, controller, integration
* Add session key routing, email allowlist, and config plumbing
Route to the actual OpenClaw session via x-openclaw-session-key header
instead of creating isolated sessions. Gate external assistant access
behind an email allowlist (EXTERNAL_ASSISTANT_ALLOWED_EMAILS env var).
Plumb session_key and allowedEmails through Helm chart, compose, and
env template.
* Add HTTPS_PROXY support to External::Client for Pipelock integration
Net::HTTP does not auto-read HTTPS_PROXY/HTTP_PROXY env vars (unlike
Faraday). Explicitly resolve proxy from environment in build_http so
outbound traffic to the external assistant routes through Pipelock's
forward proxy when enabled. Respects NO_PROXY for internal hosts.
* Add UI fields for external assistant config (Setting-backed with env fallback)
Follow the same pattern as OpenAI settings: database-backed Setting
fields with env var defaults. Self-hosters can now configure the
external assistant URL, token, and agent ID from the browser
(Settings > Self-Hosting > AI Assistant) instead of requiring env vars.
Fields disable when the corresponding env var is set.
* Improve external assistant UI labels and add help text
Change placeholder to generic OpenAI-compatible URL pattern. Add help
text under each field explaining where the values come from: URL from
agent provider, token for authentication, agent ID for multi-agent
routing.
* Add external assistant docs and fix URL help text
Add External AI Assistant section to docs/hosting/ai.md covering setup
(UI and env vars), how it works, Pipelock security scanning, access
control, and Docker Compose example. Drop "chat completions" jargon
from URL help text.
* Harden external assistant: retry logic, disconnect UI, error handling, and test coverage
- Add retry with backoff for transient network errors (no retry after streaming starts)
- Add disconnect button with confirmation modal in self-hosting settings
- Narrow rescue scope with fallback logging for unexpected errors
- Safe cleanup of partial responses on stream interruption
- Gate ai_available? on family assistant_type instead of OR-ing all providers
- Truncate conversation history to last 20 messages
- Proxy-aware HTTP client with NO_PROXY support
- Sanitize protocol to use generic headers (X-Agent-Id, X-Session-Key)
- Full test coverage for streaming, retries, proxy routing, config, and disconnect
* Exclude external assistant client from Pipelock scan-diff
False positive: `@token` instance variable flagged as "Credential in URL".
Temporary workaround until Pipelock supports inline suppression.
* Address review feedback: NO_PROXY boundary fix, SSE done flag, design tokens
- Fix NO_PROXY matching to require domain boundary (exact match or .suffix),
case-insensitive. Prevents badexample.com matching example.com.
- Add done flag to SSE streaming so read_body stops after [DONE]
- Move MAX_CONVERSATION_MESSAGES to class level
- Use bg-success/bg-destructive design tokens for status indicators
- Add rationale comment for pipelock scan exclusion
- Update docs last-updated date
* Address second round of review feedback
- Allowlist email comparison is now case-insensitive and nil-safe
- Cap SSE buffer at 1 MB to prevent memory blowup from malformed streams
- Don't expose upstream HTTP response body in user-facing errors (log it instead)
- Fix frozen string warning on buffer initialization
- Fix "builtin" typo in docs (should be "built-in")
* Protect completed responses from cleanup, sanitize error messages
- Don't destroy a fully streamed assistant message if post-stream
metadata update fails (only cleanup partial responses)
- Log raw connection/HTTP errors internally, show generic messages
to users to avoid leaking network/proxy details
- Update test assertions for new error message wording
* Fix SSE content guard and NO_PROXY test correctness
Use nil check instead of present? for SSE delta content to preserve
whitespace-only chunks (newlines, spaces) that can occur in code output.
Fix NO_PROXY test to use HTTP_PROXY matching the http:// client URL so
the proxy resolution and NO_PROXY bypass logic are actually exercised.
* Forward proxy credentials to Net::HTTP
Pass proxy_uri.user and proxy_uri.password to Net::HTTP.new so
authenticated proxies (http://user:pass@host:port) work correctly.
Without this, credentials parsed from the proxy URL were silently
dropped. Nil values are safe as positional args when no creds exist.
* Update pipelock integration to v0.3.1 with full scanning config
Bump Helm image tag from 0.2.7 to 0.3.1. Add missing security
sections to both the Helm ConfigMap and compose example config:
mcp_tool_policy, mcp_session_binding, and tool_chain_detection.
These protect the /mcp endpoint against tool injection, session
hijacking, and multi-step exfiltration chains.
Add version and mode fields to config files. Enable include_defaults
for DLP and response scanning to merge user patterns with the 35
built-in patterns. Remove redundant --mode CLI flag from the Helm
deployment template since mode is now in the config file.
* First cut of a simplified "intro" UI layout
* Linter
* Add guest role and intro-only access
* Fix guest role UI defaults (#940)
Use enum predicate to avoid missing role helper.
* Remove legacy user role mapping (#941)
Drop the unused user role references in role normalization
and SSO role mapping forms to avoid implying a role that
never existed.
Refs: #0
* Remove role normalization (#942)
Remove role normalization
Roles are now stored directly without legacy mappings.
* Revert role mapping logic
* Remove `normalize_role_settings`
* Remove unnecessary migration
* Make `member` the default
* Broken `.erb`
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
* Use dependent: :purge_later for user profile_image cleanup
This is a simpler alternative to PR #787's callback-based approach.
Instead of adding a custom callback and method, we use Rails' built-in
`dependent: :purge_later` option which is already used by FamilyExport
and other models in the codebase.
This single-line change ensures orphaned ActiveStorage attachments are
automatically purged when a user is destroyed, without the overhead of
querying all attachments manually.
https://claude.ai/code/session_01Np3deHEAJqCBfz3aY7c3Tk
* Add dependent: :purge_later to all ActiveStorage attachments
Extends the attachment cleanup from PR #787 to cover ALL models with
ActiveStorage attachments, not just User.profile_image.
Models updated:
- PdfImport.pdf_file - prevents orphaned PDF files from imports
- Account.logo - prevents orphaned account logos
- PlaidItem.logo, SimplefinItem.logo, SnaptradeItem.logo,
CoinstatsItem.logo, CoinbaseItem.logo, LunchflowItem.logo,
MercuryItem.logo, EnableBankingItem.logo - prevents orphaned
provider logos
This ensures that when a family is deleted (cascade from last user
purge), all associated storage files are properly cleaned up via
Rails' built-in dependent: :purge_later mechanism.
https://claude.ai/code/session_01Np3deHEAJqCBfz3aY7c3Tk
* Make sure `Provider` generator adds it
* Fix tests
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Use Accept-Language for unauthenticated locale
* Add per-user locale overrides
* Fix test
* Use more than the top `accept-language` entry
* Localization of string
* Initial sec
* Update PII fields
* FIX add tests
* FIX safely read plaintext data on rake backfill
* Update user.rb
* FIX tests
* encryption_ready? block
* Test conditional to encryption on
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Implement dynamic role assignment for new family creators.
Introduced `User.role_for_new_family_creator` to assign `super_admin` to the first user of an instance and a configurable fallback role (e.g., `admin`) to subsequent users. Updated controllers and tests accordingly.
* Update default fallback role for family creators to admin.
---------
Co-authored-by: luckyPipewrench <luckypipewrench@proton.me>
- Added `has_many :sso_audit_logs` association to `User` model with `dependent: :nullify`.
- Updated `Demo::DataCleaner` to clear SSO audit logs before destroying related data.
Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
* Fix record violation
and add toggle for recurring feature
* Run only once per sync cycle ( 30 sec )
* FIX params passing
* Add collapsible to recurring section
* FIX preferences error catch
* Initial implementation
* Add support for reports section too
* UI Improvement
now it looks a lot nicer :)
* Remove duplicate section titles
* FIX malformed DIV
* Add accessibility and touch support
WCAG 2.1 Level AA Compliant
- Keyboard operable (Success Criterion 2.1.1)
- Focus visible (Success Criterion 2.4.7)
- Name, Role, Value (Success Criterion 4.1.2)
Screen Reader Support
- Clear instructions in aria-label
- Proper semantic roles
- State changes announced via aria-grabbed
* Add proper UI for tab highlight
* Add keyboard support to collapse also
* FIX js errors
* Fix rabbit
* FIX we don't need the html
* FIX CSRF and error handling
* Simplify into one single DB migration
---------
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
* Add OpenID Connect login support
* Add docs for OIDC config with Google Auth
* Use Google styles for log in
- Add support for linking existing account
- Force users to sign-in with passoword first, when linking existing accounts
- Add support to create new user when using OIDC
- Add identities to user to prevent account take-ver
- Make tests mocking instead of being integration tests
- Manage session handling correctly
- use OmniAuth.config.mock_auth instead of passing auth data via request env
* Conditionally render Oauth button
- Set a config item `configuration.x.auth.oidc_enabled`
- Hide button if disabled
---------
Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Signed-off-by: soky srm <sokysrm@gmail.com>
Co-authored-by: sokie <sokysrm@gmail.com>
* Replace Maybe for Sure in select code areas
* Make sure passwords are consistent
* Remove (admin|member) from demo data first name
* Database and schema names finally to `sure`
* Fix broken test
* Another (benchmarking) database name to `sure_*`
* More rebranding to Sure
* Missed this Maybe mention in the same page
* Random nitpicks and more Maybes
* Demo data accounts and more Maybes
* Test data account updates
* Impersonation test accounts
* Consistency with `compose.example.yml`
* Add customizable menu order for user accounts
Introduces a MenuOrder model and concern to allow users to select their preferred account ordering (by name or balance, ascending or descending). Adds a default_order field to users, updates user preferences UI, and applies the selected order to balance sheet account listings.
* Rename MenuOrder to AccountOrder and update user order field
Refactors the MenuOrder model to AccountOrder and updates all references accordingly. Replaces the user's default_order field with default_account_order, including migration changes, validations, and form fields. Updates localization and schema to reflect the new naming.
* Update balance_sheet.rb
* Fix for nil Current.user when rake runs in balance_sheet model
---------
Signed-off-by: Aluisio Pereira <oaluiser@gmail.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
- Replace API keys with OAuth2 tokens for mobile apps
- Add device tracking and management for mobile sessions
- Implement 30-day token expiration with refresh tokens
- Add MFA/2FA support for mobile login
- Create dedicated auth endpoints (signup/login/refresh)
- Skip CSRF protection for API endpoints
- Return plaintext tokens (not hashed) in responses
- Track devices with unique IDs and metadata
- Enable seamless native mobile experience without OAuth redirects
This provides enterprise-grade security for the iOS/Android apps while maintaining a completely native authentication flow.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* OAuth
* Add API test routes and update Doorkeeper token handling for test environment
- Introduced API namespace with test routes for controller testing in the test environment.
- Updated Doorkeeper configuration to allow fallback to plain tokens in the test environment for easier testing.
- Modified schema to change resource_owner_id type from bigint to string.
* Implement API key authentication and enhance access control
- Replaced Doorkeeper OAuth authentication with a custom method supporting both OAuth and API keys in the BaseController.
- Added methods for API key authentication, including validation and logging.
- Introduced scope-based authorization for API keys in the TestController.
- Updated routes to include API key management endpoints.
- Enhanced logging for API access to include authentication method details.
- Added tests for API key functionality, including validation, scope checks, and access control enforcement.
* Add API key rate limiting and usage tracking
- Implemented rate limiting for API key authentication in BaseController.
- Added methods to check rate limits, render appropriate responses, and include rate limit headers in responses.
- Updated routes to include a new usage resource for tracking API usage.
- Enhanced tests to verify rate limit functionality, including exceeding limits and per-key tracking.
- Cleaned up Redis data in tests to ensure isolation between test cases.
* Add Jbuilder for JSON rendering and refactor AccountsController
- Added Jbuilder gem for improved JSON response handling.
- Refactored index action in AccountsController to utilize Jbuilder for rendering JSON.
- Removed manual serialization of accounts and streamlined response structure.
- Implemented a before_action in BaseController to enforce JSON format for all API requests.
* Add transactions resource to API routes
- Added routes for transactions, allowing index, show, create, update, and destroy actions.
- This enhancement supports comprehensive transaction management within the API.
* Enhance API authentication and onboarding handling
- Updated BaseController to skip onboarding requirements for API endpoints and added manual token verification for OAuth authentication.
- Improved error handling and logging for invalid access tokens.
- Introduced a method to set up the current context for API requests, ensuring compatibility with session-like behavior.
- Excluded API paths from onboarding redirects in the Onboardable concern.
- Updated database schema to change resource_owner_id type from bigint to string for OAuth access grants.
* Fix rubocop offenses
- Fix indentation and spacing issues
- Convert single quotes to double quotes
- Add spaces inside array brackets
- Fix comment alignment
- Add missing trailing newlines
- Correct else/end alignment
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix API test failures and improve test reliability
- Fix ApiRateLimiterTest by removing mock users method and using fixtures
- Fix UsageControllerTest by removing mock users method and using fixtures
- Fix BaseControllerTest by using different users for multiple API keys
- Use unique display_key values with SecureRandom to avoid conflicts
- Fix double render issue in UsageController by returning after authorize_scope\!
- Specify controller name in routes for usage resource
- Remove trailing whitespace and empty lines per Rubocop
All tests now pass and linting is clean.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add API transactions controller warning to brakeman ignore
The account_id parameter in the API transactions controller is properly
validated on line 79: family.accounts.find(transaction_params[:account_id])
This ensures users can only create transactions in accounts belonging to
their family, making this a false positive.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Signed-off-by: Josh Pigford <josh@joshpigford.com>
Co-authored-by: Claude <noreply@anthropic.com>
* Save work
* Subscriptions and trials domain
* Store family ID on customer
* Remove indirection of stripe calls
* Test simplifications
* Update brakeman
* Fix stripe tests in CI
* Update billing page to show subscription details
* Remove legacy columns
* Complete billing settings page
* Fix hardcoded plan name
* Handle subscriptions for self hosting mode
* Lint fixes
* Onboarding redirect tests and trial status bar
* use helper method
* Fix time tolerance failure
* Update post-onboarding message to be generic
* Disable turbo frames on Trial start button
* Update flash notice in test
* AI sidebar
* Add chat and message models with associations
* Implement AI chat functionality with sidebar and messaging system
- Add chat and messages controllers
- Create chat and message views
- Implement chat-related routes
- Add message broadcasting and user interactions
- Update application layout to support chat sidebar
- Enhance user model with initials method
* Refactor AI sidebar with enhanced chat menu and interactions
- Update sidebar layout with dynamic width and improved responsiveness
- Add new chat menu Stimulus controller for toggling between chat and chat list views
- Improve chat list display with recent chats and empty state
- Extract AI avatar to a partial for reusability
- Enhance message display and interaction styling
- Add more contextual buttons and interaction hints
* Improve chat scroll behavior and message styling
- Refactor chat scroll functionality with Stimulus controller
- Optimize message scrolling in chat views
- Update message styling for better visual hierarchy
- Enhance chat container layout with flex and auto-scroll
- Simplify message rendering across different chat views
* Extract AI avatar to a shared partial for consistent styling
- Refactor AI avatar rendering across chat views
- Replace hardcoded avatar markup with a reusable partial
- Simplify avatar display in chats and messages views
* Update sidebar controller to handle right panel width dynamically
- Add conditional width class for right sidebar panel
- Ensure consistent sidebar toggle behavior for both left and right panels
- Use specific width class for right panel (w-[375px])
* Refactor chat form and AI greeting with flexible partials
- Extract message form to a reusable partial with dynamic context support
- Create flexible AI greeting partial for consistent welcome messages
- Simplify chat and sidebar views by leveraging new partials
- Add support for different form scenarios (chat, new chat, sidebar)
- Improve code modularity and reduce duplication
* Add chat clearing functionality with dynamic menu options
- Implement clear chat action in ChatsController
- Add clear chat route to support clearing messages
- Update AI sidebar with dropdown menu for chat actions
- Preserve system message when clearing chat
- Enhance chat interaction with new menu options
* Add frontmatter to project structure documentation
- Create initial frontmatter for structure.mdc file
- Include description and configuration options
- Prepare for potential dynamic documentation rendering
* Update general project rules with additional guidelines
- Add rule for using `Current.family` instead of `current_family`
- Include new guidelines for testing, API routes, and solution approach
- Expand project-specific rules for more consistent development practices
* Add OpenAI gem and AI-friendly data representations
- Add `ruby-openai` gem for AI integration
- Implement `to_ai_readable_hash` methods in BalanceSheet and IncomeStatement
- Include Promptable module in both models
- Add savings rate calculation method in IncomeStatement
- Prepare financial models for AI-powered insights and interactions
* Enhance AI Financial Assistant with Advanced Querying and Debugging Capabilities
- Implement comprehensive AI financial query system with function-based interactions
- Add detailed debug logging for AI responses and function calls
- Extend BalanceSheet and IncomeStatement models with AI-friendly methods
- Create robust error handling and fallback mechanisms for AI queries
- Update chat and message views to support debug mode and enhanced rendering
- Add AI query routes and initial test coverage for financial assistant
* Refactor AI sidebar and chat layout with improved structure and comments
- Remove inline AI chat from application layout
- Enhance AI sidebar with more semantic HTML structure
- Add descriptive comments to clarify different sections of chat view
- Improve flex layout and scrolling behavior in chat messages container
- Optimize message rendering with more explicit class names and structure
* Add Markdown rendering support for AI chat messages
- Implement `markdown` helper method in ApplicationHelper using Redcarpet
- Update message view to render AI messages with Markdown formatting
- Add comprehensive Markdown rendering options (tables, code blocks, links)
- Enhance AI Financial Assistant prompt to encourage Markdown usage
- Remove commented Markdown CSS in Tailwind application stylesheet
* Missing comma
* Enhance AI response processing with chat history context
* Improve AI debug logging with payload size limits and internal message flag
* Enhance AI chat interaction with improved thinking indicator and scrolling behavior
* Add AI consent and enable/disable functionality for AI chat
* Upgrade Biome and refactor JavaScript template literals
- Update @biomejs/biome to latest version with caret (^) notation
- Refactor AI query and chat controllers to use template literals
- Standardize npm scripts formatting in package.json
* Add beta testing usage note to AI consent modal
* Update test fixtures and configurations for AI chat functionality
- Add family association to chat fixtures and tests
- Set consistent password digest for test users
- Enable AI for test users
- Add OpenAI access token for test environment
- Update chat and user model tests to include family context
* Simplify data model and get tests passing
* Remove structure.mdc from version control
* Integrate AI chat styles into existing prose pattern
* Match Figma design spec, implement Turbo frames and actions for chats controller
* AI rules refresh
* Consolidate Stimulus controllers, thinking state, controllers, and views
* Naming, domain alignment
* Reset migrations
* Improve data model to support tool calls and message types
* Tool calling tests and fixtures
* Tool call implementation and test
* Get assistant test working again
* Test updates
* Process tool calls within provider
* Chat UI back to working state again
* Remove stale code
* Tests passing
* Update openai class naming to avoid conflicts
* Reconfigure test env
* Rebuild gemfile
* Fix naming conflicts for ChatResponse
* Message styles
* Use OpenAI conversation state management
* Assistant function base implementation
* Add back thinking messages, clean up error handling for chat
* Fix sync error when security price has bad data from provider
* Add balance sheet function to assistant
* Add better function calling error visibility
* Add income statement function
* Simplify and clean up "thinking" interactions with Turbo frames
* Remove stale data definitions from functions
* Ensure VCR fixtures working with latest code
* basic stream implementation
* Get streaming working
* Make AI sidebar wider when left sidebar is collapsed
* Get tests working with streaming responses
* Centralize provider error handling
* Provider data boundaries
---------
Co-authored-by: Josh Pigford <josh@joshpigford.com>
* Change email address
* Email confirmation
* Email change test
* Lint
* Schema reset
* Set test email sender
* Select specific user fixture
* Refactor/cleanup
* Remove unused email_confirmation_token
* Current user would never be true
* Fix translation test failures