74 Commits

Author SHA1 Message Date
daveadmin 3f7d4eef13 feat(tools): add letter length + summary depth controls; harden korrespond §-discipline
- Summarize: new depth param (brief/standard/detailed) with depth-aware prompt
  instructions and coverage mandate; wired through API + JS
- Korrespond: new letter length param (concise/standard/detailed) injected as
  Lengde: instruction in draft pass; wired through API + JS
- Korrespond draft prompt: add §-discipline rule (cite only directly relevant §§)
  plus Opphevet guard (aligned with dobetterlegal-tools)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 13:44:02 +02:00
daveadmin 9e707661af feat(mcp): expose ai_summary in dbn_get_document response
LEFT JOIN doc_summaries so the document object includes ai_summary
and summary_model alongside id/title. Returns null for docs not yet
backfilled.
2026-06-02 21:40:00 +02:00
daveadmin 7fcd317205 feat(tools): reposition as Do Better Legal two-track Norwegian-law MCP
De-family-ify shared JSON tools (persona-aware routing + neutral base
prompt), make the verification review pick its engine per track
(family/child-welfare -> dbn-legal-agent-v3, others -> gpt-4o interim),
and route product-name strings through dbnToolsProductName(). Rebrand the
MCP/tools surface (mcp.php + i18n mcp_* strings) to Do Better Legal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-02 07:45:17 +02:00
daveadmin d156f8cf6b feat(tools): persona selector across standalone tools + dashboard chat
Wire the legal-domain persona picker into corpus, deep-research, korrespond and
the dashboard chat. Each endpoint reads the chosen profile, resolves its packages
against client 57, and scopes retrieval via package_ids (falling back to family
when omitted). New dashboard tenants now subscribe to all DBN domain packages so
persona switching survives the subscription intersection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 23:03:31 +02:00
daveadmin 662fbf7d6d feat(tools): persona-driven multi-domain corpus + model routing
Generalize the family-locked legal tools into caveauAI persona profiles
(client 57 chat profiles, resolved in-process via the chat_profiles bridge).
Each tool accepts an optional `profile` slug that scopes the corpus package(s),
search method, system prompt and synthesis model; omitting it falls back to the
family-legal package so existing behaviour is unchanged.

- dbnToolsResolvePersona / dbnToolsListPersonas / dbnToolsBootChatProfiles in
  bootstrap.php; new api/personas.php + dbn.list_personas MCP tool.
- LegalTools search/ask/corpusContextForSummarize and the BvjAnalyzer /
  LegalAnalysis / translate paths take the persona's packages + prompt + model.
- Persona <select> on ask/search/summarize (populated from api/personas.php).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 20:49:58 +02:00
daveadmin 5a0ef89dca feat(mcp): expose corpus_search, korrespond_refine, extract_text tools
Restores the 3 tools (manifest + invoke arms + invokeExtract helper),
the citation-atom RAG lever in LegalTools/corpus-search, and the catalog
icons. These were live on prod via rsync but uncommitted, so a git-pull
deploy reverted the manifest from 22 to 19 tools.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 16:45:41 +02:00
daveadmin 28997c4b99 fix: full_name→display_name in versions+folders SQL; DOMContentLoaded in DMS pages 2026-05-26 23:31:49 +02:00
daveadmin 01a63ebf46 fix: correct Qdrant IP to 10.0.2.10 in diagnostics.php 2026-05-26 22:35:22 +02:00
daveadmin 2e2b0b45fa Full DMS: folders + ACLs, versioning, trash, bulk ops, preview, smart folders
Rebuild the dashboard as a Drive-style document management system on top of
the existing CaveauAI hybrid RAG pipeline.

Backend:
- 5 migrations (versions, trash soft-delete, saved searches, categories, audit)
- DMS helpers (folder ACL walker, disk storage, audit, version snapshot,
  XLSX/PPTX/HTML/CSV/MD extractors)
- New APIs: folders, document-versions, trash, bulk, preview, saved-searches,
  categories, diagnostics
- Extended APIs: documents (folder_id, soft-delete, ACL filter, sort),
  upload (9 file types, version-collision detection with replace/new/keep-both,
  disk persistence), chat-stream (folder scoping + graph related-documents)
- 30-day trash purge cron with Qdrant + disk + graph cleanup

Frontend:
- Drive-style two-pane browser with folder tree, drag-drop, bulk-action bar,
  right-click context menu, multi-select
- New pages: folders (tree + per-folder ACL editor), trash (restore/purge)
- Extended pages: upload (folder picker, version-collision modal, 9 file
  type chips), document (Preview/Versions/Permissions tabs with PDF.js +
  mammoth.js + audio), index (DMS KPIs + activity feed), settings (live
  diagnostics ping MariaDB/Qdrant/LiteLLM/FalkorDB/disk), chat (folder
  scope chips + related-authorities chips)
- New CSS (dms.css) + JS bundle (dms.js) exposing window.DBN_DMS
- Sidebar nav adds Folders + Trash items

All routes return HTTP 200 in local smoke test; all 32 files lint clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 22:24:56 +02:00
daveadmin 234ab7278b Timeline: group same-date/actor events, clean badges, Bedrock routing
- renderTimeline(): group consecutive same-date+actor events into one card
  with a bullet list; single events keep their current layout
- Date format: YYYY-MM-DD → "1 Jun 2023" (3-letter month, international)
- Time shown in header when available
- Remove date_type badge; confidence badge replaced by amber ⚠ flag on
  low-confidence events only (high/medium border colour still shows)
- LegalTools.php: resolve azure_full/azure_mini to Bedrock Sonnet/Haiku
  when DbnBedrockGateway is active; claude_sonnet/claude_haiku also handled
- timeline.php + api/timeline.php: engine labels updated (Claude Haiku/Sonnet);
  claude_haiku + claude_sonnet added to valid engine list
- i18n engine labels updated in all 4 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:21:35 +02:00
daveadmin b2e1bf268d korrespond v1 premiere: Bedrock routing, engine picker, journal auto-save + status
- KorrespondAgent: add resolveDeployment() helper; fix classify/translate to use
  Haiku via Bedrock, draft to use Haiku (quick) or Sonnet (thorough) — fixes broken
  withDeployment('gpt-4o-mini') calls when DBN_BEDROCK_ENABLED=true
- korrespond.php: add Quick/Thorough engine picker (case_toggle already present)
- korrespond.js: pass engine in request payload
- api/korrespond.php: accept user-selected engine, auto-save to case_tool_results
  for paid users after each successful run, update deployment log label
- CaseResults: add korr_status to listForUser SELECT, add updateStatus() method
- result-action.php: add set_status action for correspondence journal
- account.php: show status dropdown (Draft/Sent/Reply received/Resolved) for
  korrespond entries in #analyses, wire JS change handler to result-action.php

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 21:52:09 +02:00
daveadmin 8205a22205 Fix legal analysis issue extraction for long documents 2026-05-25 19:06:23 +02:00
daveadmin 17ad54cf36 Add chunked timeline routing 2026-05-25 12:34:41 +02:00
daveadmin 75b19f1dcf Add timeline engine size routing 2026-05-25 11:33:47 +02:00
daveadmin f00d3d68e5 Add Quick mode (nova-lite/Bedrock) as 3rd tier for timeline tool
Timeline now offers Quick/Standard/Deep: nova_lite routes to Amazon
Bedrock nova-lite via LiteLLM (1 credit, ~2s faster), azure_mini stays
gpt-4o-mini (1 credit), azure_full stays gpt-4o (2 credits, Pro only).
ToolModels tier rules: free→nova_lite only, plus→quick/standard,
pro→all three.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 10:26:07 +02:00
daveadmin d47024ed67 timeline: remove GPU, add SSE status updates, DOCX export, single-file, engine-aware credits
- Remove GPU/cuttlefish engine from timeline.php, api/timeline.php, LegalTools.php, tools.js (all 4 langs)
- Add engine-aware credit cost: gpt-4o-mini=1 credit, gpt-4o=2 credits (matches redact pattern)
- Remove multiple attribute from file input (single document only)
- New api/timeline-stream.php: SSE endpoint emitting status events + final result
- New api/timeline-download.php: DOCX export of timeline events
- LegalTools::timeline() gains ?callable $onProgress for live status updates
- tools.js: spinner on run, SSE streaming fetch, Export to Word button
- Save to My Docs was already wired (showSaveResultButton at line 1136)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 09:32:28 +02:00
daveadmin 4b8b675a64 redact: unify save to single 'Save to My Docs' button; fix DOCX content type
- Remove js-save-corpus button from redact output (was failing with 'no_workspace'
  for users without a linked CaveauAI workspace)
- Single save path now goes through showSaveResultButton() → api/case/save-result.php,
  which works for all paid (Plus/Pro) users without workspace dependency
- Relabel 'Save result' → 'Save to My Docs' and update success message
- Fix DOCX: contentTypesXml() had wrong ContentType for docProps/core.xml
  (application/package/... → application/vnd.openxmlformats-package.core-properties+xml);
  Word validates this strictly and was rejecting the file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:40:39 +02:00
daveadmin 56cd87dd7b redact: UX overhaul — engine simplification, credits, spinner, save-to-docs, badges
- Remove GPU/regex engine options; keep only azure_mini (1 credit) and azure_full (2 credits)
- Variable credit cost: engine-aware pre-check and charge in api/redact.php; PricingCatalog base = 1
- Fix ATTORNEY not preserved when keepOfficials=true: add to LLM prompt, generic-tag, pseudonym regexes
- Replace Azure credits hint with per-engine credit cost text (all 4 languages)
- Single-file upload only (was: up to 5); simplify status messages
- Clear previous redaction output and show pulsing spinner when a new run starts
- Add "Save to My Docs" button in redact output panel (corpus-save.js path)
- corpus-save.js: capture source_doc_ids from button dataset, pass in POST payload
- api/save-to-corpus.php: accept source_doc_ids, store first as source_url=corpus-doc:{id}
- doc-picker.js: show "✂ Redacted" badge for documents saved from the redact tool
- CSS: .redact-working spinner, doc-item__badge--redact pill styles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 08:18:51 +02:00
daveadmin b912ff22bc Dashboard account section, profile API, and CSS account panels
- SSO session auth gating on all protected pages
- dashboard.php: account section (profile form + workspace panel),
  onboarding prompt modal, overview bar extracted to CSS classes,
  dashboard.css linked in page head
- api/profile.php: save/dismiss endpoint for optional profile fields
- assets/css/dashboard.css: account grid, dash-account-panel,
  dash-profile-form, profile-prompt-backdrop modal, overview bar
  classes, dash-section-kicker, dash-tier-badge base styles
- includes/bootstrap.php: dbnToolsMainUserProfile,
  dbnToolsProfileNeedsPrompt, dbnToolsRequirePageAuth
- scripts/sql/004_user_profile_fields.sql: nullable phone, address,
  and profile_prompt_dismissed_at columns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 18:49:34 +02:00
daveadmin b21bfb2f1d Add NOK pricing catalog, credit ledger, success-based charging, and tier-gated model routing
- PricingCatalog.php: single source of truth for plans (free/plus/pro), top-ups,
  Stripe price env keys, tool costs (0–6 credits), STT variable billing, feature limits
- FreeTier.php: monthly-first credit deduction, ledger (user_tool_credit_ledger),
  STT reservation/settle/release, monthly reset, trial logic
- StripeClient.php: canonical SKUs (plus/pro/topup_100/300/1000), legacy aliases kept
- stripe-checkout.php: subscription vs payment mode, trial gating, catalog metadata
- stripe-webhook.php: idempotent via stripe_events, handles subscription lifecycle +
  invoice.paid renewal + one-time topup credit grants
- All API tools: success-based credit deduction (check before, charge after)
- transcribe.php: file-size heuristic reservation, settle from actual provider duration
- ask.php + LegalTools.php: ToolModels engine resolution — Pro gets gpt-4o
- KorrespondAgent.php + korrespond.php: tier-gated draft deployment —
  Free/Plus gets gpt-4o-mini, Pro gets gpt-4o
- pricing.php: NOK-only, plan cards, top-up packs, Organisation contact card,
  tool cost table, separate monthly/prepaid balance display
- 003_pricing_credit_catalog.sql: ledger and STT reservation tables

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:42:27 +02:00
daveadmin bffc714541 Add My Docs picker to deep-research, advocate, barnevernet, korrespond, citations
- PHP: Add docPickerSection (button + chips + hidden input) to all 5 tool pages
- JS: Send doc_ids in payload for deep-research, advocate, barnevernet, korrespond
- Backend: Inject selected corpus doc content into paste_text/narrative/notes via dbnToolsInjectDocContent
- Citations: Add upload zone (file → api/extract.php → textarea) + paste textarea with live Norwegian legal reference extraction (regex) + ref chips → title search; doc picker populates titleInput via MutationObserver

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:04:45 +02:00
daveadmin c997f204b5 feat: add Do Better Norge MCP server — token system, runtime API, interactive setup page
- UserMcpTokens: per-user SHA256-hashed token mint/validate/revoke (Plus/Pro only)
- DbnMcpRuntime: 19 MCP tools (search, ask, summarize, timeline, redact, translate,
  legal_analysis, korrespond, barnevernet_analyze, advocate_brief, deep_research,
  discrepancy_find, transcribe_audio, corpus_stats, list_documents, get_document,
  citation_graph, case_workbench_plan, save_to_case)
- api/mcp/user/: session/tools/invoke HTTP endpoints with Bearer token auth
- api/mcp-tokens.php: token create/revoke/list REST API
- mcp.php: interactive setup page with token management, 5-client config tabs,
  auto-fill on token creation, tool catalog grid, privacy notice
- account.php: simplified MCP section with link to mcp.php
- nav.php: MCP nav link
- .htaccess: Authorization header passthrough, MCP route rewrite, CORS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 11:01:13 +02:00
daveadmin 0bb828fb98 fix: require AzureOpenAiGateway.php in api/translate.php 2026-05-24 10:03:00 +02:00
daveadmin effd3289b4 feat: add Legal Translation tool (translate.php)
New dedicated tool for translating Norwegian legal documents (Barnevernet
letters, court decisions, correspondence) into the user's chosen language
with legal-terminology annotations.

- translate.php: new tool page with source/target language selectors,
  4-way UI lang switcher, file upload, doc picker, streaming results
- api/translate.php: NDJSON streaming endpoint; Azure GPT-4o-mini with
  legal-aware prompt that preserves Norwegian statute refs verbatim and
  annotates terms with no target-language equivalent; 2-credit cost
- assets/js/translate.js: form handler, NDJSON stream reader, copy button
- assets/css/tools.css: .lt-* styles for translation result + annotations
- includes/i18n.php: 22 lt_* keys × 4 languages; translate entry in nav
- includes/FreeTier.php: translate → 2 credits
- includes/CaseResults.php + case-result.php: translate in eligible tools,
  toolLabel, toolIcon, deriveTitle, rendering block, rerun map

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 09:59:06 +02:00
daveadmin 7e6463ed22 Add Legal Analysis tool — two-pass DBN-legal pipeline
Restores the dbn-legal-agent-v3 fine-tune on ocelot (was silently aliased
to plain qwen2.5:14b in LiteLLM since the viper retirement) and ships a
new tool that uses it via a two-pass flow:

  Pass 1 (Azure 4o-mini)  → extract up to 5 distinct legal issues
  Pass 2 (ocelot v3 only) → answer each issue, ≤350 tokens, with corpus
  Pass 3 (Azure 4o-mini)  → synthesise overall assessment + next steps

The 12GB-VRAM constraint motivates the split: dbn-legal-agent-v3 stays
hot in VRAM through the 5 sequential per-issue calls because issue
extraction and synthesis run on Azure, not on ocelot.

New surface:
  - includes/LegalAnalysisAgent.php
  - api/legal-analysis.php           (NDJSON streaming endpoint)
  - legal-analysis.php               (dedicated tool page)
  - assets/js/legal-analysis.js      (streamed UI with per-issue cards)
  - Save-result + case-result.php rendering for legal-analysis output
  - Nav registration in all four UI languages

Add-on integration: a "⚖️🇳🇴 Run deep legal analysis on this text"
button now appears on Summarize, Ask, and Redact result pages and
streams the same pipeline inline below the existing result.

Existing tools relabelled: the misleading "🇳🇴 Norwegian specialist v3 "
option on advocate/deep-research/discrepancy/barnevernet is now honestly
"DBN Legal Agent" — now that the real fine-tune is actually deployed,
the label finally matches reality. The advocate.php v2 option was
removed since the v2 GGUF is retired.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 04:21:01 +02:00
daveadmin 2013648ee0 Add manual 'Save result' to all tools — replaces auto-save
All tool results can now be saved to My Case manually. Users click
'Save result', type a description, and confirm. This replaces the
previous silent auto-save on barnevernet/timeline/etc., giving users
control over what stays and what it's called (supports multiple runs
of the same tool with different titles).

- CaseResults: extend ELIGIBLE_TOOLS to include summarize, ask, redact,
  transcribe; add toolLabel/toolIcon entries; support explicit title
  via meta['title'] in save()
- api/case/save-result.php: new client-initiated save endpoint;
  accepts tool + title + input_payload + output_payload + meta
- Remove CaseResults::save() auto-save from barnevernet, deep-research,
  discrepancy, korrespond, timeline API endpoints
- tools.js: add showSaveResultButton() (exposed as window.dbnShowSaveResultButton);
  wire for ask, redact, timeline, transcribe (both file-upload and
  stored-audio paths)
- barnevernet.js: wire save button after final result render
- summarize.js: wire save button after renderFinal(); passes sumResults
  container so widget appears in the correct #sumResults div
- case-result.php: rich tool-specific rendering for summarize, ask,
  redact, transcribe, timeline; update re-run link map to include all
  new tools
- tools.css: styles for .save-result-widget and its states (idle,
  prompt, done, error)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 01:27:26 +02:00
daveadmin 0fcfed1a86 fix: allow empty text field when doc_ids supplied to all tool APIs
dbnToolsString was called with required=true (default), so it aborted
before dbnToolsInjectDocContent could inject content from the doc picker.
Now passes required=false and validates after injection so doc-only
submissions work in timeline, redact, ask, and summarize.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 00:39:46 +02:00
daveadmin e768662efe Add Summarize Document tool — engine selector, file upload, optional corpus enrichment
- summarize.php: full custom inline form (replaces tool_form.php wrapper) with
  lang switcher, azure_mini/azure_full/gpu engine selector, 8 corpus-slice
  toggles (all off by default), doc picker, file upload zone, and textarea
- api/summarize.php: rewritten to streaming NDJSON (matches barnevernet pattern);
  accepts JSON payload with text, language, engine, slices[], doc_ids[]
- includes/LegalTools.php: adds corpusContextForSummarize() (keyword search via
  ClientRagPipeline) and summarizeWithContext() (engine-aware LLM call with
  optional corpus prepend); returns structured JSON matching existing summarize format
- assets/js/summarize.js: self-contained IIFE handling file upload via
  api/extract.php, slice toggles, NDJSON stream reader, result rendering,
  and trace panel update
- includes/i18n.php: adds 'summarize' to nav in all 4 languages (EN/NO/UK/PL),
  inserted after 'redact' in the tool order with icon 'SZ'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 23:25:40 +02:00
daveadmin f383ad5b74 feat: document & audio corpus picker for all tools
- Add "Select from My Docs" button to all text tool forms; free-tier
  users see an upgrade modal, paid (CaveauAI) users get a searchable
  multi-select modal backed by /api/dashboard/documents.php
- Add "Select from My Audio" picker on Transcribe with single-select
  and a "Save to My Audio" button for persisting uploaded clips
- New PHP helpers in bootstrap.php: dbnToolsFetchDocChunks,
  dbnToolsClientIdFromSession, dbnToolsInjectDocContent
- timeline, ask, redact APIs prepend selected document content
  (fetched from client_chunks SQL) before the textarea text
- api/dashboard/audio-upload.php stores audio files on server and
  creates a client_documents row with source_type='audio'
- api/transcribe.php falls back to stored audio via audio_doc_id POST
  field when no file is uploaded
- api/dashboard/documents.php supports ?source_type= filter
- tools.js: doc_ids added to JSON payload; stored-audio transcribe path
- New assets/css/doc-picker.css, assets/js/doc-picker.js
- SQL migration: scripts/sql/audio_docs_column.sql

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 21:38:04 +02:00
daveadmin 90117fa9de feat(nav): unified navbar, account page, corpus summary widget, and i18n fixes
- New includes/nav.php: sticky site-wide nav with Tools dropdown, Dashboard
  link, compact language switcher, user identity → /account.php, Log out
- New account.php: credits & plan, profile, team, usage sections
- New api/corpus-summary.php: JSON endpoint for corpus doc count + last updated
- Replaces topbar in layout.php, layout_dashboard.php, and dashboard.php
- Fixes hardcoded Norwegian strings in dashboard.php credit cards via dbnToolsT()
- Adds 35 new i18n keys across all 4 languages (en/no/uk/pl) in i18n.php
- CSS: .dbn-nav navbar + .account-* account page styles in tools.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:11:39 +02:00
daveadmin 33dc5406b2 feat(auth): add login/logout, user identity, and soft auth gate
- api/logout.php: destroys session + clears cookie, redirects to /
- api/guest-session.php: sets guest flag, lets users explore without account
- layout.php: removes hard PHP redirect; authenticated users see email +
  "Logg ut" in topbar; guests see guest banner (sticky, dismissible) and
  auth gate modal (dismissible via localStorage) instead of redirect
- layout_footer.php: injects auth gate modal + JS for banner/modal dismiss
- layout_dashboard.php: adds username + "Logg ut" to dash-topbar
- index.php: adds "Utforsk uten konto" link under primary login CTA
- tools.css: .guest-banner, .auth-gate-*, .topbar-user, .dash-topbar__user

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 18:05:51 +02:00
daveadmin 06d01a3bce feat(dashboard): add corpus dashboard at /dashboard/
Full private corpus dashboard for tools.dobetternorge.no users — each SSO
account gets an auto-provisioned CaveauAI tenant (clients row, corpus) on
first visit. Includes upload (file/paste/URL), RAG chat with SSE streaming
and citation chips, document CRUD, FalkorDB graph relations tab, and
improved save-from-tool flow with tag/preview support.

- dashboard/{index,documents,document,upload,chat,settings}.php
- api/dashboard/{corpus-init,documents,upload,ingest-status,chat-stream,
  save-from-tool,graph}.php
- includes/{CorpusProvision,layout_dashboard,layout_dashboard_footer}.php
- assets/css/dashboard.css  assets/js/corpus-save.js (routing upgrade)
- includes/{bootstrap,layout}.php extended for dashboard provisioning

Migration 141 (clients.dbn_sso_uid + import_method enum) applied on chloe.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 17:15:40 +02:00
daveadmin 83fc71414f Add premium My Case MVP 2026-05-23 10:17:34 +02:00
daveadmin b014638f39 feat(corpus): add save-to-corpus + private corpus search scope
- POST /api/save-to-corpus.php — saves tool output text to user's default CaveauAI corpus via ClientRagPipeline
- api/case/upload.php — dual-writes uploaded PDFs to CaveauAI client_documents (best-effort)
- assets/js/corpus-save.js — shared <dialog> handler for .js-save-corpus buttons on all tool pages
- includes/layout_footer.php — injects corpus-save.js + shared save dialog markup
- korrespond/deep-research/barnevernet/discrepancy JS — save-to-corpus buttons on output sections
- api/search.php + LegalTools::search() — corpus_scope param ('shared'|'private'|'both'), merges personal CaveauAI corpus with shared legal library when 'both'
- includes/tool_form.php + assets/js/tools.js — corpus scope radio toggle shown on search tab
- api/user-docs.php — add POST upload method for non-SSO authenticated users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 17:50:32 +02:00
daveadmin ba9cddf9a1 Add monetization spine + Build Your Own Case (Min Sak)
- Stripe: StripeClient.php, checkout/portal/webhook endpoints, idempotent event handling
- FreeTier: tier-aware credits (free/light/pro/pro_plus), bonus_balance, hourly caps per tier
- pricing.php + billing.php: 4-tier cards, 3 topups, Customer Portal, balance breakdown
- Min Sak: CaseStore.php, AzureDocIntelligence.php, AzureSearchAdmin.php — per-user hybrid RAG
- api/case/: upload, list, delete, ingest-callback (HMAC-auth'd from n8n)
- award-survey-credits: inter-site HMAC endpoint for dobetternorge.no survey bonus
- dashboard.php: tier badge, balance breakdown card, Min Sak CTA, survey CTA
- KorrespondAgent + all 3 other agents: use_my_case toggle wired to dbnToolsCaseContext()
- bootstrap.php: dbnToolsCaseContext(), dbnToolsIntersiteSecret(), dbnToolsCurrentTier()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 20:52:54 +02:00
daveadmin 28932297b3 Add user context notes field to timeline tool
Adds an optional textarea below the main text input where users can provide
clarifications to guide the LLM — e.g. year anchors, actor aliases, or focus
instructions. Notes are injected into the prompt as a clearly delimited block
and translated across all four UI languages (en/no/uk/pl).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:36:37 +02:00
daveadmin dfb9692f45 Korrespond: stop mixing UI languages — all chrome follows user UI lang
Drafts still come back in Norwegian + working language (that is intentional),
but every piece of *chrome* now respects the user's UI lang consistently:

- Pass 1 classify LLM now writes missing-fact questions in the user's language
  (not always Norwegian), fixing the case where an English-UI user got "Hva er
  saksnummeret?" in the clarify panel.
- All PHP-emitted progress/status messages go through DbnKorrespondAgent::L()
  with en/no/pl/uk variants instead of hardcoded Norwegian.
- JS introduces an I18N dictionary + t() helper covering status messages,
  button labels, column headers, flag labels, refine panel title/hint,
  jurisdiction radio labels, clarify panel title/hint/buttons, the empty-state
  "Ready" block, and Copy/Copied/Download .txt.
- Static clarify and empty-state chrome use [data-i18n] attributes resolved at
  init and re-applied on every lang-switcher click.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 12:11:16 +02:00
daveadmin 5d8ae6b447 Korrespond: add Refine pass with jurisdiction-scoped formal citations
After the first draft is rendered, a "Refine with citations" panel offers a
3rd-pass rewrite scoped to the user's choice of Norwegian law, ECHR (EMK +
HUDOC case law), or both. Refine pulls fresh corpus chunks limited to the
chosen jurisdiction's slices, rewrites inline cites in formal style ("jf.
forvaltningsloven § 17", "jf. Strand Lobben m.fl. mot Norge, EMD-37283/13,
§§ 207–214"), and appends a Rettskilder block listing every authority.
Hard-RAG grounding carries through — refine cannot cite anything that
wasn't retrieved. Costs 1 additional credit; the original draft stays in
place and the refined version appears below it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 11:50:36 +02:00
daveadmin b78a49e060 Add Korrespond tool: drafts replies & new correspondence to NO authorities
Two-pass wizard for drafting to NAV, Barnevernet, schools, Bufdir, kommune,
Statsforvalter, Trygderetten. Pass 1 (gpt-4o-mini) classifies the situation
and emits clarify questions if facts are missing; user answers inline and
resubmits without losing context. Pass 2 retrieves law passages via hard-RAG
(ClientRagPipeline with body-specific slice presets), drafts in Norwegian
bokmål with gpt-4o using [CITE:N] tokens, self-checks that every citation
maps to a real corpus passage, then translates to the working language.
Result is side-by-side Norwegian + EN/PL/UK with copy/download per side
and an expandable Cited Law panel.

Credit deducts only when Pass 2 actually runs, not on a clarify cycle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 11:27:13 +02:00
daveadmin bc44b0eee2 Add My Documents panel to workbench + user-docs API
- api/user-docs.php: GET/DELETE shared dbn_user_docs table (SSO users only)
  connects to dobetternorge DB via DBN_DB_* env vars
- workbench.php: My Documents panel (section 05) for SSO/free-tier users;
  shows docs uploaded from either AI chat or tools, links to AI Chat for upload
- workbench.js: fetch + render doc list, delete with Qdrant cleanup
- tools.css: workbench-docs panel + item styles
- i18n.php: my_docs_* strings in all 4 languages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 09:37:19 +02:00
daveadmin e977bbb6b3 Add Document Discrepancy Finder tool
8-step NDJSON-streaming pipeline that compares two Barnevernet documents:
classifies each doc, extracts parties and timelines, cross-references both
for contradictions/deletions/additions, retrieves corpus legal context, and
synthesises a full discrepancy report with tabbed UI.

New files: DiscrepancyAgent.php, api/discrepancy.php, discrepancy.php,
discrepancy.js. Modified: FreeTier.php (cost=4), i18n.php (all 4 langs),
tool-svgs.php (DC icon), tools.css (dc-* component styles).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:30:38 +02:00
daveadmin 2e2dfd7310 feat(corpus): category filter, passage expand, drill enhancements, URL hash state
- Search: category filter pills scope results to a legal domain
- Search: full chunk text returned; click to expand inline beyond 600-char excerpt
- Drill panel: total count label ("Showing X of Y"), sort dropdown, title filter (300ms debounce)
- URL hash: preserves query/mode/lang/category/drill state for bookmarking

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:47:56 +02:00
daveadmin ffcf887428 feat(timeline): add live filter, actor chips, group headers, copy button, source toggle, count badge
- Live search/filter bar: filters events by keyword across event, actor, source_excerpt, date
- Actor filter chips: click to filter by actor, multi-select, teal active state
- Year/month group headers when sorted chronologically (── 2023 ──, Mar 2024 ──)
- Per-event copy button (hover-revealed 📋): copies "date · actor · event" to clipboard
- "Hide/show sources" toggle: collapses all source excerpts without re-rendering
- Count badge: "23 events · 3 actors · 2022–2025" above the list
- applyTimelineFilters() unifies sort + actor + text filters in one re-render pass
- CSV export now includes end_date column
- Reset all filter state on each new run

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:46:59 +02:00
daveadmin c4362738c1 feat(transcribe): GPT cleanup pass + advanced options i18n
Adds optional post-transcription cleanup via GPT-4o/GPT-4o-mini to fix
mishearing errors, punctuation, and domain terms. Speaker role labelling
now accepts a deployment param. Adds i18n strings for advanced options
panel (task, VAD filter, Whisper model, AI cleanup) in all four languages.
Updates BvjAnalyzerAgent and DeepResearchAgent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 07:23:01 +02:00
daveadmin 8b77acb828 feat: free-tier credit system + Syttende Mai access for Google users
- FreeTier.php: credit check/deduct/reset engine with hourly rate limit
- bootstrap.php: dbnmDb() singleton, dbnToolsIsFreeTier(), credit gate helpers
- index.php: store tier=free|approved in session from SSO JWT
- All 7 API endpoints: credit gate (402/429) + X-Credits-Remaining header
- layout.php: credit meta tag, JS balance var, Syttende Mai banner (05-17 only)
- tools.js: credit badge in topbar, 402 modal, 429 toast, dbnUpdateCredits()
- barnevernet.js + deep-research.js: wire 402/429 handling for NDJSON streams
- tools.css: styles for credit badge, no-credits modal, rate-limit toast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:05:08 +02:00
daveadmin 568314c554 fix: wire GCP Speech client into tools transcribe (was using unreachable ai-portal path)
Copies GcpSpeechClient into the tools repo so it's deployed with the code;
removes the broken dbnToolsAiPortalRoot() path that resolved to a nonexistent
/home/dobetternorge/ai-portal directory. Also restarted the CPU Whisper
service which had a stuck CLOSE_WAIT socket causing silent fetch failures.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:43:28 +02:00
daveadmin 08d1e3cee3 feat: auto-select STT engine (Azure → Google Cloud → Whisper) and show provider in results
Removes user-facing engine/model/key/beam controls. The server now picks
the best available engine automatically:
1. Microsoft Azure Speech — short clips (≤1MB, no diarization, audio/*)
2. Google Cloud Speech v2 — long audio, diarization, all languages
3. OpenAI Whisper GPU — local fallback

Results display which provider was used (e.g. "Transcribed with Google
Cloud Speech") via transcript-engine-badge and traceMeta.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 13:22:24 +02:00
daveadmin 43cf5b8ce4 feat: Barnevernet Analyzer — document analysis + partisan RAG brief
7-step agent pipeline: document classification, party extraction, timeline
extraction, corpus RAG (child_welfare/echr/family_core/bufdir_guidance),
and synthesis using the user's chosen engine (including dbn-legal-agent).
Progressive NDJSON streaming renders doc_meta, parties, and timeline cards
before the final advocacy brief and procedural red flags arrive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 20:49:46 +02:00
daveadmin 343b19d0b4 Add sub-question branching + document summary modals
- Source modal now shows LLM-generated document summary (lazy-gen + cached
  in documents.summary) instead of raw chunk text; toggle reveals matched
  chunk; "View all chunks" button fetches every chunk of the document via
  new api/document-chunks.php endpoint
- Each sub-question card gets a "Branch ↓" button that pre-fills the query
  with that sub-question and shows a context panel with the prior brief
  summary; prior_context + branch_notes are injected into interpretSeed()
  and synthesise() so the LLM knows where the research is coming from
- Upload document summaries generated at synthesis time and attached to
  upload sources alongside corpus summaries
- DB: documents.summary TEXT column added to bnl_corpus on chloe

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 19:44:27 +02:00
daveadmin a61329eb85 Route Whisper to chloe localhost (127.0.0.1:20019)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 18:56:02 +02:00